2011-11-27 06:15:35 +01:00
|
|
|
Coding Guidelines
|
|
|
|
|
=================
|
2024-11-21 18:07:30 +01:00
|
|
|
:Date: Autumn 2024
|
2023-04-04 06:11:36 +02:00
|
|
|
:toc:
|
2011-11-27 06:15:35 +01:00
|
|
|
|
|
|
|
|
|
2024-11-21 18:07:30 +01:00
|
|
|
.Basic Tenet
|
|
|
|
|
Code is written for humans. Use your judgement and common sense above all.
|
|
|
|
|
|
|
|
|
|
|
2011-11-27 06:15:35 +01:00
|
|
|
|
|
|
|
|
Style Guide
|
|
|
|
|
-----------
|
|
|
|
|
The Lumiera project uses GNU indentation style with slight adaptations.
|
|
|
|
|
|
2024-11-21 18:07:30 +01:00
|
|
|
- *no tabs* please. +
|
|
|
|
|
The typical ``semi indent'' of GNU style thus becomes 2 spaces.
|
2022-10-01 18:04:59 +02:00
|
|
|
- maximum line length is rather around *110 characters*.footnote:[This is not a hard
|
|
|
|
|
limit, rather a guideline -- however, you should never try to stuff several distinct
|
|
|
|
|
topics into a single line...]
|
2014-10-15 23:08:53 +02:00
|
|
|
- originally, GNU style focused on plain-C code. +
|
2011-11-27 06:15:35 +01:00
|
|
|
We thus apply some relaxations and clarifications ...
|
2025-06-07 23:59:57 +02:00
|
|
|
|
2011-11-27 06:15:35 +01:00
|
|
|
* the braces for a class scope are indented by 2 spaces
|
|
|
|
|
* the access modifiers start at this brace level, while all declarations and definitions
|
|
|
|
|
within the class are again indented by 2 spaces
|
2024-11-21 18:07:30 +01:00
|
|
|
* the braces for free-standing functions however are not indented and thus placed directly
|
|
|
|
|
below the name of the function in the definition. This holds also for member functions
|
|
|
|
|
defined out-line and not within the class body.
|
2011-11-27 06:15:35 +01:00
|
|
|
* the line breaking rules are relaxed. Definitions and statements may be written as single line,
|
2024-11-21 18:07:30 +01:00
|
|
|
provided that the length remains below 110 chars and the result _remains legible_.
|
|
|
|
|
More specifically...
|
2025-06-07 23:59:57 +02:00
|
|
|
|
2011-11-27 06:15:35 +01:00
|
|
|
** function declarations may be written in one line
|
|
|
|
|
** same for definitions with just a single statement in the body
|
|
|
|
|
** same for simple if-statements without else-branch.
|
2024-11-21 18:07:30 +01:00
|
|
|
** If in doubt however, we prefer to wrap the lines.
|
2025-06-07 23:59:57 +02:00
|
|
|
|
2024-11-21 18:07:30 +01:00
|
|
|
* it is OK to write if-then-else without braces, but only as long as the structure
|
|
|
|
|
is uttermost clear and poses no danger for maintenance. Use your common sense on this one.
|
|
|
|
|
Often, a ternary operator is a viable alternative. When a `?` ... `:` becomes long and
|
|
|
|
|
hard to read, the alternative clause starting with `:` is placed directly below the `?`
|
2011-11-27 06:15:35 +01:00
|
|
|
* the space between function name and opening parenthesis of the argument list is not
|
2024-11-21 18:07:30 +01:00
|
|
|
enforced when this doesn't make sense, notably for argument-less functions, chained calls
|
2011-11-27 06:15:35 +01:00
|
|
|
or constructor syntax. But in all other cases, we really value this additional space,
|
|
|
|
|
it improves legibility.
|
|
|
|
|
* template argument declarations are _always_ written on a separate line, above the
|
|
|
|
|
return type declaration. This rule holds even if the rest of a definition can be
|
|
|
|
|
written within a single line.
|
2024-11-21 18:07:30 +01:00
|
|
|
* the body of λ-functions is indented beyond the capture clause, i.e. it is rather
|
|
|
|
|
starkly offset with respect to the regular code. When capture clauses become complicated
|
|
|
|
|
beyond just a short list of names -- especially when including initialisers -- they are
|
2025-06-07 23:59:57 +02:00
|
|
|
preferably written on a separate line, directly above the argument list in parenthesis
|
2011-11-27 06:15:35 +01:00
|
|
|
* the opening brace of namespaces is placed on the same line. Optionally, the namespace
|
|
|
|
|
body may be indented, as long as this helps underpinning the nesting structure. But
|
|
|
|
|
there is no need to use 3 indents on a 3 level nested namespace. One level is enough
|
2011-12-10 01:41:21 +01:00
|
|
|
to highlight the presence of a nesting.
|
2011-11-27 06:15:35 +01:00
|
|
|
|
2024-11-21 18:07:30 +01:00
|
|
|
- the shorthand type names `uint`, `ulong`, `uchar`, `short`, `ushort`, `llong`, `ullong`
|
|
|
|
|
are preferred, as it is clearer to write the fundamental type in a single word. Index
|
|
|
|
|
variables _should always be unsigned._ Use `size_t` whenever applicable.footnote:[yet it is OK
|
|
|
|
|
to use `uint` in a for-loop where the limits are clear; likewise it is acceptable to use
|
|
|
|
|
a direct conversion signed ⟷ unsigned when the situation is simple and obvious. No need
|
|
|
|
|
to be pedantic on everything]
|
|
|
|
|
When the bit-size matters, please use explicit designations like `int32_t`, `uint64_t`.
|
|
|
|
|
- we prefer to highlight the _typing as distinct_ from the names and entities; notably
|
|
|
|
|
the `*` pointer and `&` reference modifier are attached post-fix to the type, as opposed
|
|
|
|
|
to sticking it to the name: write ```int* thing`'', not ```int *thing`''. Whenever types
|
|
|
|
|
become difficult to parse, we introduce a type alias (`using Alias =`... with an uppercase
|
|
|
|
|
type alias name). This is especially relevant with function pointers, where we prefer
|
|
|
|
|
to define a type alias for the _function signature_ first.
|
|
|
|
|
- in a similar vein, we distinguish between an ```const object`'' which _is really in-itself_
|
|
|
|
|
unmodifiable, as opposed to _taking a const-reference,_ which is always spelled out
|
|
|
|
|
post-fix as `object const&`, or a constant-pointer spelled as `const*`.
|
|
|
|
|
- we use the textual form of the boolean operators `and`, `or` and `not`, because this
|
|
|
|
|
allows to make the code resemble complete meaningful sentences in human language.
|
|
|
|
|
We _do use_ the »line noise« variant however for all low-level bit manipulation,
|
|
|
|
|
`&`,`|`, `^`, sometimes also for `!` -- using those is taken as an indication of
|
|
|
|
|
entering the ``danger zone''...
|
|
|
|
|
|
2024-11-23 17:21:41 +01:00
|
|
|
|
|
|
|
|
Spelling
|
|
|
|
|
~~~~~~~~
|
|
|
|
|
Lumiera uses _British spelling._ Please set your spell checker accordingly.
|
|
|
|
|
|
2011-11-27 06:15:35 +01:00
|
|
|
Naming conventions
|
|
|
|
|
~~~~~~~~~~~~~~~~~~
|
2011-12-10 01:41:21 +01:00
|
|
|
Naming conventions are used to characterise the kind of element at hand and give a visual
|
2011-11-27 06:15:35 +01:00
|
|
|
clue to the reader. We use our own conventions -- there is no point in arguing that this
|
|
|
|
|
and that library or language uses other conventions.
|
|
|
|
|
|
|
|
|
|
- type names start with an uppercase letter
|
|
|
|
|
- variable and function names start with lowercase.
|
|
|
|
|
- fields within a class, especially the private ones are decorated with a trailing underscore
|
2014-10-15 23:08:53 +02:00
|
|
|
- a leading underscore may be used to emphasise the strictly internal or technical nature of a type,
|
2011-11-27 06:15:35 +01:00
|
|
|
variable or function
|
|
|
|
|
- namespaces are all-lowercase
|
|
|
|
|
- macros and constants are preferably all-caps (at least where this makes sense)
|
|
|
|
|
|
|
|
|
|
There is a preference for *CamelCase* -- yet underscores are acceptable, especially when the
|
|
|
|
|
name is more like a sentence than just a compound term.
|
|
|
|
|
|
|
|
|
|
plain-C names
|
|
|
|
|
^^^^^^^^^^^^^
|
|
|
|
|
Since C has no namespaces, we strictly require a +lumiera_+ prefix on all non-local names and definitions.
|
|
|
|
|
Generally, names should be formed according to the following pattern:
|
|
|
|
|
|
|
|
|
|
namespace[_object][_verb[_subjects]][_version]
|
|
|
|
|
|
|
|
|
|
In case a definition actually denotes an object, there should be
|
|
|
|
|
|
|
|
|
|
- a basic struct name: `typedef struct namespace_foo_struct namespace_foo;`
|
|
|
|
|
- plus an object pointer/handle: `typedef namespace_foo* NamespaceFoo;`
|
|
|
|
|
|
|
|
|
|
The object pointer/handle should be passed as 1^st^ argument with the name +self+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
General Code Arrangement and Layout
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
- Headers and translation units are named `*.hpp` and `*.cpp` rsp. `*.h` and `*.c` +
|
|
|
|
|
Multilingual headers are called `*.h`
|
|
|
|
|
- Each header should be named according to the primary facility it exposes. For the filesystem name,
|
|
|
|
|
the +CamelCaseWords+ of this type are translated into +camel-case-words.hpp+
|
|
|
|
|
- Each file should start with the GNU licence preamble. The headline should give a one-line summary.
|
|
|
|
|
The primary author(s) and the year of the initial copyright claim should be mentioned.
|
2014-10-15 23:08:53 +02:00
|
|
|
- Each header should be focused on a specific purpose. Preferably it starts with a file-level
|
2011-11-27 06:15:35 +01:00
|
|
|
doxygen comment explaining the intention and anything not obvious from reading the code.
|
2018-10-27 17:27:29 +02:00
|
|
|
At lest a `@file` tag with one line of classification in a doxygen comment at the top of every
|
2023-04-04 06:11:36 +02:00
|
|
|
file is mandatory.footnote:[This rule stands simply because, without such a file-level doxygen
|
2018-10-27 17:27:29 +02:00
|
|
|
comment, doxygen will _ignore all contents_ of this file (really, might be surprising, yet it is
|
|
|
|
|
the way it is...)]
|
2011-11-27 06:15:35 +01:00
|
|
|
- when arranging headers and compilation units, please take care of the compilation times and the
|
|
|
|
|
code size. Avoid unnecessary includes. Use forward declarations where applicable.
|
2015-05-28 03:05:49 +02:00
|
|
|
Yet still, _all immediately required direct dependencies should be mentioned_, even if already
|
|
|
|
|
included by another dependency. See the extensive discussion of these
|
|
|
|
|
link:{ldoc}/technical/code/linkingStructure.html#_imports_and_import_order[issues of code organisation]
|
2011-11-27 06:15:35 +01:00
|
|
|
- The include block starts with our own dependencies, followed by a second block with the library
|
|
|
|
|
dependencies. After that, optionally some symbols may be brought into scope (through +using+ clauses).
|
2023-04-04 06:11:36 +02:00
|
|
|
Avoid cluttering top-level namespaces. Never import full namespaces.footnote:[No `using namespace gtk;`
|
2018-10-27 17:27:29 +02:00
|
|
|
or `using namespace boost` please! Experience shows, in the end you'll be using 5 names or so, but
|
|
|
|
|
pull in all the others just for sake of laziness. Just type the f**g `using` clause for every
|
|
|
|
|
import individually, and we'll all be better off...]
|
2011-11-27 06:15:35 +01:00
|
|
|
- the includes for our own dependencies shall be given relative to source-root (or test root). Don't use
|
|
|
|
|
relative includes for headers located in the same directory, or -- worse still -- in the parent directory.
|
2023-04-04 06:11:36 +02:00
|
|
|
- sometimes, the actual translation units will combine several facilities for technical reasons.footnote:[To
|
2023-03-16 01:16:37 +01:00
|
|
|
elaborate, there can be ``headers'', which are in fact only intended for inclusion at one or two distinct
|
2022-10-01 18:04:59 +02:00
|
|
|
places. This should be mentioned in the file-level comment, but generally is an acceptable practice,
|
|
|
|
|
and better then lumping everything into a 1000 lines header. As a guideline, if you expect a rather
|
|
|
|
|
technical concern not to be of much interest for most readers of a header, then better extract it into
|
|
|
|
|
a separate self-contained header and include it. E.g., you might be sharing an implementation-level
|
|
|
|
|
class or even singleton instance and some constant definitions.
|
|
|
|
|
Just be sure not to include definitions several times.]
|
2011-11-27 06:15:35 +01:00
|
|
|
Anonymous namespaces should be used liberally to avoid unnecessary exports.
|
|
|
|
|
- template code mostly needs to reside in headers. (same for metaprogramming code).
|
|
|
|
|
We employ the simple inclusion model (``Borland model'') for template instantiation.
|
|
|
|
|
- But in some specific situations it is preferable to drive explicit instantiations from within
|
|
|
|
|
a +*.cpp+ file. Most notably this is the case when defining some core class hierarchies.
|
|
|
|
|
Such explicit instantiations should be limited to just a view obvious places. They should be
|
|
|
|
|
written into a block at the end of some central implementation file. See +assetmanager.cpp+
|
|
|
|
|
for an example.
|
2022-10-01 18:04:59 +02:00
|
|
|
- deliberately there is __no single top-level namespace.__footnote:[We do not want to encourage
|
|
|
|
|
the emergence of _central locations,_ where global definitions for each and everyone might be
|
|
|
|
|
stuffed and buried. If you want to share something, then please create an interface and expose
|
|
|
|
|
a service, together with definitions for your clients to use.]
|
|
|
|
|
The +namespace lumiera+ is the root of our _exported interfaces_ -- intended for use by external
|
|
|
|
|
scripts and libraries. Everything implementation related is arranged in per-subsystem trees of
|
|
|
|
|
namespaces. The APIs of the subsystems are exported explicitly into the +lumiera+ namespace.
|
2011-11-27 06:15:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
Design Guidelines and Values
|
|
|
|
|
----------------------------
|
|
|
|
|
Code is written for *being read by humans*; code shall convey its meaning even to the casual reader.
|
|
|
|
|
On the long run, this language nature of code is more important than any performance tweaks. Recall,
|
|
|
|
|
every idiot can figure out how to make a computer perform something. Yet the real challenge is to
|
|
|
|
|
write legible code. Code that operates exactly the way you'd guess just from reading it.
|
|
|
|
|
Black magic and all kinds of surprise trickery and cleverness are nothing to be proud off.
|
|
|
|
|
|
|
|
|
|
-> please have a look at the link:/project/background/CleanCodeDevelopment.html[Clean Code] page
|
|
|
|
|
for a coherent system of design principles
|
|
|
|
|
|
2023-04-04 06:11:36 +02:00
|
|
|
Recommendations at code level
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
|
|
- *Inversion of Control* is the leading design principle
|
|
|
|
|
+
|
|
|
|
|
* ``don't call us, we call you...''
|
|
|
|
|
* avoid lumping everything into a single point-and-shot action
|
|
|
|
|
* decompose into _Services_ meaningful as such, which are self-contained and can be tested;
|
|
|
|
|
_ask_ for services instead of fumbling with other part's innards.
|
|
|
|
|
* avoid _shared data models_ and _coordination via flags_ -- prefer messaging
|
|
|
|
|
and represent processes and interactions as first-class entities.
|
|
|
|
|
+
|
|
|
|
|
- clearly distinguish between *value semantics* and *reference semantics*
|
|
|
|
|
+
|
|
|
|
|
* if something can be distinguished as an entity, has a distinct identity, needs to be built,
|
|
|
|
|
managed and tracked, then treat it with reference semantics.
|
|
|
|
|
* always consider the _ownership_ and _lifecycle_ -- value objects can not have any, should
|
|
|
|
|
ideally be immutable, and stored inline or avoid heap allocation. If in doubt, decompose
|
|
|
|
|
and turn unclear and changing parts into a service / dependency, attached by (value) handle
|
2024-11-21 18:07:30 +01:00
|
|
|
* objects with reference semantics should be made _non-copyable_, while value objects should use
|
2023-04-04 06:11:36 +02:00
|
|
|
default copy semantics. Use 'lib/nocopy.hpp' to express the flavours of move only / no copy.
|
|
|
|
|
* equality comparisons of ref objects are to be based on their identity solely, while for
|
|
|
|
|
value objects, all properties must be included which are tangible and independently variable.
|
|
|
|
|
+
|
|
|
|
|
- make liberal use of interfaces and abstractions; provide points of access
|
|
|
|
|
+
|
|
|
|
|
* if implementation instances with reference semantics need to be obtained or registered,
|
|
|
|
|
then create a static factory function called `create` or `register` or `attach`
|
|
|
|
|
* if some dependency is ``just required'', then use a static accessor `instance`, which
|
|
|
|
|
can be implemented with Lumiera's dependency helper 'lib/depend.hpp'
|
|
|
|
|
+
|
|
|
|
|
- avoid implicit assumptions -- better express them as type
|
|
|
|
|
+
|
|
|
|
|
* if is something is ``just a number'' yet has some specific meaning,
|
|
|
|
|
better use a lightweight wrapper object to tag it with semantics
|
2024-11-21 18:07:30 +01:00
|
|
|
* if a common pattern works by involving distinct, unrelated entities,
|
2023-04-04 06:11:36 +02:00
|
|
|
then better use generic programming or even higher-kinded types,
|
|
|
|
|
instead of forcing unrelated types to inherit from some supertype.
|
|
|
|
|
* avoid downcasts, `void*` and switch-on-type programming; this
|
|
|
|
|
programming style bears an attitude of carelessness and tends to produce
|
|
|
|
|
highly tangled, scattered code hard to maintain over time.footnote:[There
|
|
|
|
|
are valid exceptions to that rule though; in some parts, Lumiera has to deal
|
|
|
|
|
with low-level and high-performance computing where every extra byte needs
|
|
|
|
|
to be justified. However, in such cases it is always possible to _encapsulate_
|
|
|
|
|
the performance critical parts into _opaque types,_ which are clearly fenced
|
|
|
|
|
by visibility rules and thus recognisable as highly coupled implementation details.]
|
|
|
|
|
+
|
|
|
|
|
- care for lifecycle issues
|
|
|
|
|
+
|
|
|
|
|
* avoid performing anything non-local during the startup- or shutdown phase
|
|
|
|
|
* especially avoid as much as possible to do anything substantial in destructors
|
|
|
|
|
* if something depends on contextual state, better make that explicit
|
|
|
|
|
* however -- even better to avoid state at all: prefer a builder or use a new type
|
|
|
|
|
at the point of state transition
|
|
|
|
|
+
|
|
|
|
|
- be precise and consider error situations at every point
|
|
|
|
|
+
|
|
|
|
|
* if you feel like assuming something, document this through assertions
|
|
|
|
|
* if you can not be sure, then better check and abort by throwing
|
|
|
|
|
* be prepared for exceptions to be thrown everywhere -- embrace RAII
|
|
|
|
|
* exceptions are fine, but never build logic on top of exceptions or depend
|
|
|
|
|
on them: the so called ``checked exceptions'' tend to create unnecessary coupling.
|
|
|
|
|
* use exceptions only for exceptional situations, never for signalling something.
|
|
|
|
|
* only _handle_ errors which you _really can handle right here and now_
|
|
|
|
|
* avoid to ``fix'' or ``amend'' a situation by assumptions -- _let it crash_
|
|
|
|
|
* however -- ``crash'' always means a clean wind-down -- never leak anything
|
|
|
|
|
|
|
|
|
|
|