Code guidelines
The following guidelines must be followed by all new code. Existing code that does not yet follow should be adapted when possible. You might disagree with some guidelines, but in a large code base as this one consistency is more important than personal opinion. All guidelines have a short identifier label, e.g. N.1, for easier reference in discussions.
For cases and constructs not explicitly mentioned here, code should fall back to the C++ Core Guidelines.
ACTS-specific
A.indices: Always use enum values to access vector/matrix components
Always use the appropriate enum values to access components. This clearly states the semantic meaning and is easier to understand.
Example:
// local surface coordinate vector
Vector2 loc;
loc[ePos0] = 2.0;
// space-time coordinate vector
Vector4 pos4;
pos4[ePos0] = 0.1;
...
pos4[eTime] = 12.3;
// bound parameters vector
BoundVector pars;
pars[eBoundLoc0] = 1.2;
...
pars[eBoundPhi] = M_PI;
...
Do not use plain numbers as this can lead to inconsistencies if the order changes. It reduces the readability and makes it harder to spot errors.
Do not use the .{x,y,z,w}()
accessors available for small fixed-size
vectors. While this should produce correct results in most cases, the accessor
name does not always match the semantic meaning, e.g. for momentum vectors, and
it only works for vectors but not for matrices. Always use the entries in
enum CoordinateIndices
for a consistent way of accessing both vectors and matrices.
Naming
N.1: Namespaces use CamelCase
Namespaces use CamelCase with an upper case initial.
Example:
namespace Something {
...
}
The only exception is the nested detail
namespace with always uses the lower-case name. There is no particular strong reason but existing practice for this. The detail
namespace must be never be the top-most namespace.
Example:
namespace Foo {
namespace detail {
...
}
}
N.2: Concrete types use CamelCase
All concrete types, i.e. classes, structs, enums, and typedefs for both fundamental and external types, use CamelCase with an upper case initial.
Example:
class ComplicatedNamedClass;
struct AnotherStruct;
enum EnumType;
using SemanticName = int32;
Prefer semantic names over descriptive names. This applies in particular to typedefs of external types or standard containers. Types are checked by the compiler, while semantic meaning is only checked by the user; optimize for the latter.
Example:
// avoid: purely descriptive typename
using ConstThingPtrVector = std::vector<const Thing*>;
// prefer: semantic typename the describes the usage
using InputThings = std::vector<const Thing*>;
Avoid abbreviations and prefer full names. If abbreviations must be used, only use upper case for the initial letter of the abbreviation. The abbreviation is semantically a single word and this provides better visual consistency with the CamelCase naming.
Example:
// VertexSomething should be abbreviated as
struct VtxSomething; // not VTXSomething.
// This is bad example since the name should not be abbreviated here anyway.
N.3: Functions and methods use mixedCase
All functions and methods use mixedCase with a lower case initial. This applies also to private helper functions, e.g. in the detail
namespace. These are not fundamentally different from other functions and do not warrant a separate convention just by virtue of being e.g. in the detail
namespace.
Example:
doSomething(...);
thing.doSomething(...);
detail::anImplementationDetail(...);
N.4: Variables use mixedCase with appropriate prefix
Local variables, including function arguments, and public member variables use mixedCase with lower case initial.
Example:
int aVariable;
struct Thing {
double aPublicVariable;
float anotherPublicOne;
};
Private member variables have an additional m_
prefix.
Example:
class Foo {
private:
m_privateMember;
};
Static variables, i.e. local, global, and member ones, have an additional s_
prefix to mark them as the satanic tools that they are.
static int s_thisIsBadAndYouShouldFeelBad = -42;
class Bar {
static double s_notMuchBetter = 3.14;
};
N.5: Constant values use kCamelCase
Variables representing constant values use CamelCase with a k
prefix. Only variables marked as constexpr
are considered constant values.
Example:
static constexpr double kMagic = 1.23;
Variables defined in the Acts::UnitConstants
namespace are exempted for usability reasons and use regular variable naming instead.
N.6: Enum values use eCamelCase
Enum values use CamelCase with a e
prefix. They are not really constants but symbolic values, e.g. they can never have an address, and warant a separate convention.
Example:
enum Bar {
eBla,
eBlub,
};
N.7: Template parameter types use snake_case_t
Template parameter types use lower case words, separated by underscores, with a _t
suffix. The separate naming convention from concrete types clarifies that these are variable types and avoids naming collisions when reexporting the type.
Example:
template<typename large_vector_t>
struct Algorithm {
// make the template type publicly available
using LargeVector = large_vector_t;
...
};
N.8: Macros use ACTS_ALL_UPPER_CASE
Do not use macros. You want to use macros? Do not use them! Are you even listening? Ok, you are not: macros use UPPER_CASE
with underscores as word separators and a mandatory ACTS_
prefix.
Example:
#define ACTS_I_DID_NOT_LISTEN(...) ...
N.9: Files use CamelCase
Files use CamelCase with upper case initial. If the file defines a single class/struct, the filename must match the typename. Otherwise, a common name describing the shared intent should be used.
Source files use the .cpp
extension, Header files use the .hpp
extension, inline implementation files use the .ipp
extensions.
Files that contain only symbols in the detail
namespace should be moved in adetail/
directory in the module directory. This should not be done for corresponding source files.