Version 1.0, June 24, 2015
This is a document of the naming & formatting guidelines compiled for the STAR collaboration by the above mentioned authors. This effort was initiated by the STAR Sofwtare and Computing Leader Jerome Lauret on October 31, 2014. The charge can be viewed here. The committee produced two documents, one for the naming & formatting guidelines seen here, and one for C++ coding guidelines that can be viewed here.
The committee based their work on the existing guidelines, expanded them for clarity, and added new material where it saw fit. We have made heavy use of the Google Style guide at http://google-styleguide.googlecode.com using their xml and css style sheets. Some of the guidelines included are from the original Google document, some were modified versions by the ALICE collaboration. Where they matched the STAR guidelines we used their text to minimize efforts.
The goal of this guide is to provide a number of rules that keep the code base manageable by enforcing consistency. It is very important that any programmer can look at another programmer's code and understand it quickly. Maintaining a uniform style and following conventions means that "pattern-matching" can be more easily used to identify different symbols and invariants.
Creating common, required idioms and patterns makes code much easier to understand. In some cases there might be good arguments for changing certain style rules. Nonetheless, for reasons of consistency the rules are left unchanged.
Hooray! Now you know you can expand points to get more details. Alternatively, there are an "expand all summaries" and an "expand all summaries and extra details" at the top of this document.
The most important consistency rules are those that govern naming. The style of a name immediately informs us what sort of thing the named entity is: a type, a variable, a function, a macro, etc., without requiring us to search for the declaration of that entity. The pattern-matching engine in our brains relies a great deal on these naming rules.
Naming rules are pretty arbitrary, but we feel that consistency is more important than individual preferences in this area, so regardless of whether you find them sensible or not, the rules are the rules.
Within reason, give as descriptive a name as possible. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable to a new reader. Examples of well-chosen names:
Poorly chosen names use ambiguous abbreviations or arbitrary characters that do not convey meaning. Do not use directly the variable names from mathematical formulas. In mathematics, variable names are usually limited to a single letter. To implement a mathematical formula use variable names that clearly indicate what value it holds.
Type and variable names should typically be nouns: e.g.,
FileOpener
, numberOfErrors
.
Function names should typically be imperative (that is they
should be commands): e.g., openFile()
,
setNumberOfErrors()
.
All names in C++ code follow camel case convention. This is the practice of writing compound words so that each word begins with a capital letter. No underscores are allowed.
For example:
Note, that STAR follows the CamelCase convention even for acronyms:
StFmsHit
, StTpcHit
, etc.
Variables and functions start with a lowercase letter.
Everything else in C++ code (namespaces, type names, constant
expressions) starts with an uppercase letter.
Do not use abbreviations except for acronyms. For example:
A single letter variable name can be used for well-known idioms like iterators of integer type and pimpl-idioms (d-pointer).
St
and the appropriate three letter acronym (only first letter capitalized) to indicate its origin and prevent name clashes.
The St
will identify a STAR class to separate it from system, or external libraries classes, e.g.: StTpcTrack
.
The three letter acronym typically reflects the context within STAR in which the class operates (sub-detector, sub-project)
and reduces the possibility of name clashes for common packages. Classes unrelated to specific packages may retain only the St
prefix.
St
.
Each header file should contain only one or related class declarations for maintainability and for easier retrieval
of class definitions.
Files that contain only function code (e.g. Maker) and are unrelated to a specific class should start also with St
followed by a descriptive name of the purpose of the code.
C++ implementation files should end in .cxx
and header files
should end in .h
.
Inline functions should go directly into your
.h
file.
Examples of acceptable file names:
Having upper case letters in a file name might theoretically lead to problems for case-insensitive operating systems. However, as the file name corresponds to the class name it seems important to keep the same case as well. Moreover, having two valid class/file names that would collide on a not-case sensitive OS seems extremely unlikely.
MyClass
, MyEnum
.
The names of all types — classes, structs, typedefs, and enums — have the same naming convention. For example:
myLocalVariable
.
m
.m
prefix for struct members.g
.constexpr
variables are capitalized.const
.For example:
Data members (also called instance variables or member
variables) are prefixed with m
.
If the data member is static
, prefix the variable with s
instead.
Data members in structs are named like regular variables.
Global variables,
which should be rare in any case, are prefixed with g
.
A variable declared as constexpr
typically is only used as compile-time constant (but may be stored in read-only memory).
Therefore it uses the same convention as enum
s and uses uppercase CamelCase.
A variable declared as const
does not have any additional naming rules.
The prefixes describe the scope of the variable.
const
is just one aspect of the type of the variable.
To provide a constant in the public interface consider constexpr
,
enum
, or a (constexpr
) function instead.
myFunction()
.
get
) and mutators are prefixed with set
, for example:
myMemberVariable()
, setMyMemberVariable()
.is
or has
. Example of functions:
The rule applies also to constexpr
functions:
Accessors and mutators match
the name of the variable they are getting and setting. For the latter use
the prefixes set
. Do not use the get
prefix as done in ROOT.
The prefix set
is not exclusive for mutators and could
be used for other functions, if applicable.
This shows an excerpt of a class whose instance variable is
mNumberOfEntries
:
Functions returning a boolean value should be prefixed with
is
or has
:
This rule applies also to class member functions where is
or has
replace get
:
MyNamespace
.
St
followed by an upper case letter:
StBeamPolarizationAxis
, StDetectorId
.
class
keyword, as opposed to a traditional enum, which is unscoped and
doesn't include the class
keyword in its declaration.
Unscoped enumerators are exposed to the enclosing scope. Therefore they should be prefixed or postfixed (decided by which position makes the code more prose-like) with the enumeration's name (or a sensible part thereof). All names use uppercase CamelCase.
Example of an unscoped enumeration:
Another example of an unscoped enumeration whose enumerators are prefixed with just a (sensible) part of the type name.
Enum classes (new in C++11) create scoped enumerators. Therefore there is no need for prefixing or postfixing.
Ok
and OutOfMemory
are too generic to be added to the global namespace.
ST_PROJECT_PKG1_MY_MACRO
.
Macros must be named with all uppercase letters and underscores, prefixed with the sub/project name.
The following naming convention keeps consistency between STAR code and packages written by many users. Not only it enables users to have a feel for what-does-what but also, it allows managers to define basic default set of compilation rules depending sometimes naming convention. Naming conventions in general are fundamental to all large projects and although N users will surely have N best-solution, the rules should be enforced as much as possible.
The StRoot/
tree Is of the following form:
StRoot |
XXX/ |
Only base class that should be freely named. Example: StarClassLibrary ,
StarRoot , Star2Root .
User discouraged, request should be accompanied with a strong reasoning. |
StXXX/ |
A directory tree which will contain a base class many makers will use and derive from.
In this category, XXX can be anything. For example, StChain ,
StEvent , StEventUtilities .
User discouraged, request should be accompanied with a strong reasoning. |
|
StXXXMaker/ |
A tree for a Maker, that is, code compiled in this tree will be assembled as
one self-sufficient package. A maker is a particular class deriving from
StMaker . Its purpose is to run from within a chain (StChain )
of makers and perform a specific task.
In this category, sub-name convention are as follows:
XXX is in principle a detector sub-system identification
(3 to 4 letters uniquely designating the sub-system), it may also be anything
but a detector sub-system (StAssociationMaker ,
StMiniMcMaker , StDbMaker ) or of the
form XXX =analysis or physics study. |
|
StXXXRaw*/ |
Any directory with named with the word Raw will make our make system
include the necessary path for the Run-Time-System DAQ reader files
automatically. This convention is additive to any other description
and convention herein.
Example: |
|
StXXXUtil/ and StXXXUtilities/ |
Code compiled in a Util or Utilities tree should be code which do
not perform any action (nor a maker) but constitute by itself a set
of utility classes and functions. Other classes may depend on a
Utility library.
|
|
StXXXPool/ |
This tree will contain a set of sub-directories chosen by the user,
each sub-directory maybe a self-contained project with no relation
with anything else. Each sub-directory will therefore lead to the
creation of a separate library. The naming convention for the
library creation is as follow :
XXX can be easer a Physics Work Group acronym or a
detector sub-system acronym. |
|
StXXXClient / |
This tree will behave like the Pool trees in terms of library naming creation
(separate libraries will be created, one per compilable sub-directory).
XXX can be anything relevant for a sub-system. Client directories
MUST compile (unlike the pools) and may be part of a dependency of a data
processing chain. Its general goal is to provide a different tree structure
for a set of code providing a "service" widely used across makers.
For example, the Run Time System (RTS) have a Client tree containing DAQ
related reading codes. |
StRoot/ |
StXXX/ and /. |
README |
A basic documentation in plain text (not mandatory). If exists, the software guide will display the information contained in this file |
doc/ |
A directory containing more elaborate documentation, either in html or in LaTeX. Note that if a file named index.html exists, the software guide will link to it. | ||
local/ |
A subdirectory containing stand-alone
Makefiles for the package and/or
standalone configuration files.
This directory is persona non grata. It was introduced due to unfortunate momentary laps of reason, power surge or extreme special transitional needs. Do not use. |
||
examples/ |
A directory having a collection of code using the Maker or utility package of interest (case insensitive) | ||
macros/ |
A directory containing root macros example making use of the maker | ||
kumac/ |
This is an obsolete directory name (from staf time) but still considered by the make system. It may also appears in the pams/ tree structure. | ||
test/ |
This directory may contain test programs
(executables should in principle not
appear in our standard but be assembled)
This directory is persona non grata. It was introduced due to unfortunate momentary laps of reason, power surge or extreme special transitional needs. Do not use. |
||
html/ |
A directory possibly containing cross-linked information for Web purposes. However, note that the documentation is, since 2002, auto-generated via the doxygen documentation system (see the sofi page for more information). | ||
images/ |
A directory containing images such as
bitmap, pixmaps or other images used by
your program but NOT assembled by any
part of the build process. XPM files
necessary for Qt for example should not
be placed in this directory as explicit rules
exists in cons to handle those (but cons
will ignore the xpm placed in images/ ).
|
||
wrk/ and
run/ |
Discouraged for users. | ||
include/ |
A directory containing a set of common
include files.
This directory is persona non grata. It was introduced due to unfortunate momentary laps of reason, power surge or extreme special transitional needs. Do not use. |
||
Any other name | Will be searched for code one level down
only.
All compiled code will be assembled in
one library named after to StXXX... .
Each sub-directory will be compiled
separately that is, each must contain code
using explicit include path as the only
default search paths for includes will be
the one described by CPPPATH and its
own directory.
However, if there is a need for Include statement can ALWAYS refer to
the relative path after the
|
||
StXXXPool/ and
StXXXClient/ and
./ |
doc/ local/ examples/ macros/ kumac/ test/ html/ images/ wrk/ run/ include/
|
As noted above (i.e. the content of those
directories will be skipped by the make
system).
|
|
Any other name | The presence of every sub-directory will
create a different dynamic library. Note
that this is NOT the case with the other
name format (all compiled code would go
in a unique library name)
The convention is as follows:
|
StEventDisplay.* |
Directories within this pattern will be compiled using the extra
include path pointed by the environment variable QTDIR .
The moc program will run on any include containing the Q_OBJECT directive,
-DR__QT define is added to CXXFLAGS . |
StDbLib StDbBroker |
Those are special. Compilation will consider MySQL includes and the created dynamic library will be linked against MySQL |
St.*Db.* |
Any directory following this pattern will use the MySQL include as an extra include path for the CPPPATH directive |
StTrsMaker StRTSClient |
Are two exceptions of kind (b) [see above]
and use their own include/ directory as a general extraneous include path. |
StHbtMaker |
For this maker, a pre-defined list of sub-directories is being
added to the (CPPPATH ). |
StAssociationMaker StMuDSTMaker .*EmcUtil StEEmcPool StTofPool StRichPool Sti.*
|
This form will include in the CPPPATH every sub-directories
found one level below.
Only macros/ , examples/ , and doc/
are excluded within
this form noted in (a) [see above]. For the
Pool directory, the extraneous rule
mentioned here is additive to the one of Pool directories. |
Coding style and formatting are pretty arbitrary. However, a good project is much easier to follow if everyone uses the same style. Individuals may not agree with every aspect of the formatting rules, and some of the rules may be hard to get used to. Even so, it is important that all project contributors follow the style rules so that they can all read and understand everyone's code easily.
Try keeping lines below 100 characters. In some cases it may make sense to use much longer lines (e.g. for block editing). But this should be confined to special sections in the code.
Spaces are used for indentation. Do not use tabs in your code. You should set your editor to emit spaces when you hit the tab key. The indent of a tab depends on the setting of the editor/viewer.
The return type, function name and open parenthesis are always on the same line. The open curly brace is at the beginning of the new line after the last parameter except for inline functions.
Example:
Example of an inline function on one line:
Function/Method definition shall not be in the class declaration body. This reduces readability of the class.
Bad Example:
Good Example:
The following are examples of correctly formatted pointer and reference expressions:
When declaring a pointer or reference there are two widely used and equivalent methods: one, usually preferred in the C++ community, and another, introduced by Kerningham and Ritchie (the founders of C).
In this example, the logical AND operator is always at the end of the lines:
Example of single-argument assignments:
Note that when using auto
, initialization through braces cannot be used.
Using {}
for initialization is more consistent, more correct, and avoids
having to know about old-style pitfalls [1].
Example of variable initialization:
There is one exception: In rare cases a class may provide an std::initializer_list
constructor and other constructors that are hidden by the std::initializer_list
constructor.
The hidden constructor can then still be accessed with ()
.
Even when preprocessor directives are within the body of indented code, the directives should start at the beginning of the line.
Do not use preprocessor directives to comment out code. If it's obsolete remove it. One can always retrieve it using CVS.
public
,
protected
, private
. Declarations within a access specifier are to be ordered as
Constructor, Destructor, Methods, Data Members.
The basic format for a class declaration is:
Things to note:
public
section should be first, followed by
the protected
and finally the
private
section.
decltype
:
The preferred format for initializer lists is:
Another accepted format is:
Namespaces do not add an extra level of indentation. For example, use:
Do not indent within a namespace:
When declaring nested namespaces, put each namespace on its own line.
if
statements, for
loops etc.),
it is recommended to use curly braces even when the body of the statement fits on one line.
Technically there is no right or wrong for whitespace as C++ does not really care about white spaces. But on the other hand white spaces can make a significant difference in whether code feels foreign or easy to understand to a developer. Thus, it is important that all developers become accustomed to the same style. This makes the communication via code much easier and enjoyable for everybody involved. Using one common whitespace style throughout a project also reduces unnecessary whitespace changes and thus makes diffs easier to read.
Example:
Sometimes there are good reasons to deviate from the rules to make the code structure more visible or align similar code in different lines. Feel free to insert/remove whitespace if it increases the readability / clarity of the code.
const
before the type when defining a const variable.
Do
Putting the const
first is arguably more readable,
since it follows English in putting the "adjective"
(const
) before the "noun" (int
).
Though a pain to write, comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments.
When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous — the next one may be you!
//
or /* */
syntax, as long
as you are consistent.
You can use either the //
or the /* */
syntax; however, //
is much more common.
Be consistent with how you comment and what style you use where.
Example:
Generally a .h
file will describe the classes
that are declared in the file with an overview of what they
are for and how they are used. A .cxx
file
should contain more information about implementation details
or discussions of tricky algorithms. If you feel the
implementation details or a discussion of the algorithms
would be useful for someone reading the .h
,
feel free to put it there instead, but mention in the
.cxx
that the documentation is in the
.h
file.
Do not duplicate comments in both the .h
and
the .cpp
. Duplicated comments diverge.
Every function declaration should have comments immediately preceding it that describe what the function does and how to use. This is only necessary of the name of the function is not explicative enough.
These comments should be descriptive ("Resets the counter") rather than imperative ("Reset the counter"); the comment describes the function, it does not tell the function what to do. In general, these comments do not describe how the function performs its task. Instead, that should be left to comments in the function definition.
When commenting constructors and destructors, remember that the person reading your code knows what constructors and destructors are for, so comments that just say something like "destroys this object" are not useful. It is quite common for destructors and constructors not to have a header comment.
Each function definition should have a comment describing what the function does if there's anything tricky about how it does its job. For example, in the definition comment you might describe any coding tricks you use, give an overview of the steps you go through, or explain why you chose to implement the function in the way you did rather than using a viable alternative. For instance, you might mention why it must acquire a lock for the first half of the function but why it is not needed for the second half.
Note you should not just repeat the comments given
with the function declaration, in the .h
file or
wherever. It's okay to recapitulate briefly what the function
does, but the focus of the comments should be on how it does it.
Each class data member (also called an instance variable or member variable) should have a comment describing what it is used for. This is only necessary of the name of the variable is not explicative enough.
This for example is a unnecessary commentAs with data members, all global variables should have a comment describing what they are and what they are used for. For example:
Tricky or complicated code blocks should have comments before them. Example:
Also, lines that are non-obvious should get a comment at the end of the line. These end-of-line comments should be separated from the code by 2 spaces. Example:
Note that there are both comments that describe what the code is doing, and comments that mention that an error has already been logged when the function returns.
If you have several comments on subsequent lines, it can often be more readable to line them up:
When you pass in a null pointer, boolean, or literal integer values to functions, you should consider adding a comment about what they are, or make your code self-documenting by using constants. For example, compare:
versus:
Or alternatively, constants or self-describing variables:
Note that you should never describe the code itself. Assume that the person reading the code knows C++ better than you do, even though he or she does not know what you are trying to do:
Comments should be as readable as narrative text, with proper capitalization and punctuation. In many cases, complete sentences are more readable than sentence fragments. Shorter comments, such as comments at the end of a line of code, can sometimes be less formal, but you should be consistent with your style.
Although it can be frustrating to have a code reviewer point out that you are using a comma when you should be using a semicolon, it is very important that source code maintain a high level of clarity and readability. Proper punctuation, spelling, and grammar help with that goal.
A commented code is a rudiment from the times when there were no sophisticated revision control systems available. The developer would sometime stash a feature for later consideration right there in the code -- but often would forget about it. The other developers do not care about such unfinished or under-implemented features, which just draws their attention from the real code. Proper utilization of revision control systems allow us to save and test new features on branches without polluting the main trunk with commented code. This rule also extends to the notorious use of preprocessor directives to comment out code.
StMessage
message manager package.
The so called STAR logger is documented here.
For all messages from a given portion of code, use a unique string, like:
XXX
is either
The error codes are defined in the ERreturnCodes
enumeration.
The coding conventions described above have to be followed. However, like all good rules, these sometimes have exceptions.
To modify code that was written to specifications other than those presented by this guide, it may be necessary to deviate from these rules in order to stay consistent with the local conventions in that code. In case of doubt the original author or the person currently responsible for the code should be consulted. Remember that consistency also includes local consistency.
For example, when importing a full class from somewhere else
it is allowed to keep the naming scheme
and the style as is. To use the "correct" naming and formatting scheme (described
in this document), then to be consistent, the whole coding standard must be applied to the
whole class.
Note: in the case that the whole formatting is changed then there should be
one commit for the style changes and one for the actual code
changes.
Use common sense and BE CONSISTENT.
When editing code, take a few minutes to look at the code and determine its style.
The point about having style guidelines is to have a common vocabulary of coding so people can concentrate on what the programmer is saying, rather than on how he/she is saying it. Global style rules are presented here so people know the vocabulary. However, local style is also important. If the code added to a file looks drastically different from the existing code around it, the discontinuity throws readers out of their rhythm when they go to read it. Try to avoid this.