Coding Conventions
Content
Links to rules
- RN1 RN2 RN3 RN4 RN5 RN6 RN7 RN8 RN9 RN10 RN11 RN12 RN13 RN14 RN15 RN16 RN17 RN18 RN19 RN20 RN21 RN22
- RC1 RC2 RC3 RC4 RC5 RC6 RC7 RC8 RC9 RC10 RC11 RC12 RC13 RC14 RC15 RC16 RC17 RC18 RC19 RC20 RC22
- GC1 GC2 GC3 GC4
- RS1 RS2 RS3 RS4 RS5
Introduction
The adoption of coding conventions is one of the most important strategies to control and improve the quality of the code under development. The following list of coding conventions, including a set of rules and guidelines, provides practical indications on how to meet a common quality standard in the code style.
In the context of the collaboration between the Software Engeneering research group at ITC-Irst and the ALICE experiment, the tool RuleChecker, for the automatic verification of coding conventions, was developed. It implements most of Alice coding conventions amenable for automatic verification. In the list below, coding conventions are marked with a bullet whose color has the following meaning:
The coding convention is automatically verified by RuleChecker.
An automatic check is not feasible for the coding convention or its verification can be achieved through other tools (e.g., the compiler).
The verification of the coding convention will be implemented in a future release of the tool.
For 34 coding conventions, out of the 46 Alice conventions, an automatic verification was considered possible with RuleChecker. At the moment the tool implements the check for 30 of them, while it is to be implemented for 4.
Naming Rules
- General rules
RN1. Names should be chosen with care and should be meaningful.
RN2. In names that consist of more than one word:- The words are written together.
- The first letter of the name can be lower case or capitalized (depends on the entity).
- The first letters of the other words are always capitalized.
RN3. No special characters in names are allowed (_,#,$,&,@,-,%).Examples:
The name that consists of words alpha, beta, gamma can only be:
AlphaBetaGamma or alphaBetaGamma)AliTrack::SetThreeMomentum(float momentum);
AliTrack* fTrack;
- Header and implementation files names
RN4. Header file names are derived from the class name and have the suffix ".h" (Classname.h).
RN5. Implementation file names are derived from the class name and have the suffix ".cxx" (Classname.cxx).Example:
AliTrack.h, AliTrack.cxx
- Class names
RN6. Class names start with the prefix "Ali".
RN7. Names of detector specific classes contain the abbreviated detector name in capitals (e.g. ITS, TPC) that is placed after the standard prefix "Ali".
RN8. Class names follow the general rules RN1, RN2, RN3.Examples:
AliTPCSegment
AliCalorimeter
- Member function names
RN9. Member function names start with a capital.
RN10. Member function names follow the general rules RN1, RN2, RN3.Example:
SetThreeMomentum(float* p)- Guidelines:
GN1. Methods that make a new object which the caller must delete should start with "Create".
GN2. Methods that accept an object the caller has allocated and take responsibility for eventually deleting it must start with "Adopt".
- Guidelines:
- Data member names
RN11. Data member names start with a prefix "f".
RN12. Data member names follow the general rules RN1, RN2, RN3.Examples:
AliTPCSegment fSegment;
float fEnergy;
- Local variables names
RN13. Local variables names start with a lower case letter.
RN14. Local variables names follow the general rules RN1, RN2, RN3.Examples:
int i;
float energy;
float kineticEnergy;
- Global variables names
RN15.
Global, non-constant, objects names start with a prefix "gAli".
Global constant object names are covered by rule RN17.
RN16. Global variables names follow the general rules RN1, RN2, RN3.See also coding rule 7.
Example:
gAliRun
- Constants
RN17.
Constants, including names in enumeration types and constant statics start with a prefix "k".
Constant data members names are excluded from this rule and have to follow naming rule for special data members names RN19.
RN18. Constants names follow the general rules RN1, RN2, RN3. Examples:
const float kKineticEnergy;
enum Color { kRed, kGreen, kBlue };
- Special data members names
RN19.
Static data members names start with a prefix "fg",
constant data members names start with a prefix "fk",
static constant data members start with a prefix "fgk".
RN20. Special data members names follow the general rules RN1, RN2, RN3.Example:
class A { private: B fB; static B fgB; const B fkB; static const B fgkB; } - File Name Usage
RN21. No file names are allowed to be "hard wired" into the code.
They should be placed in one class as static data members with static public get methods.Example:
class AliFilenames { public: static str GetFileNameGaliceCards() { return fgGaliceCardsFileName; }; ... private: static str fgGaliceCardsFileName = "galice.cards"; ... };
RN22. File names in the same directory shall not differ only by the case of one or more letters.
In other words, once the file names in a given directory are all reduced to lowercase, there shall be no duplicate name
Coding Rules
RC1. The ANSI C++ standard should be followed. Additional restrictions may be adopted to make sure that the code runs on all supported platforms.
Motivation:
Following this rule ensures portability for different platforms.
RC2. The ALICE code should compile without any warnings under all supported compilers. Only warnings brought from external packages can occur.
Motivation:
Compiler warnings usually point out to imperfections that can be eliminated.
RC3. Header files begin and end with multiple-inclusion protection, as follows:
#ifndef CLASSNAME_H
#define CLASSNAME_H
...
#endif
where CLASSNAME is constructed from the name of class by converting all letters to capitals.
In order to improve readability underscores separating words are accepted.
Motivation:
This rule protects the code from the errors caused by including the same class declarations several times.
Example:
The header file of the class AliTPCTrack contains:
#ifndef ALITPCTRACK_H
#define ALITPCTRACK_H
...
#endif
Or optionally:
#ifndef ALI_TPC_TRACK_H
#define ALI_TPC_TRACK_H
...
#endif
RC4.Header files should hold the definition of a single class. It is possible to define more classes in one header file only if these classes are embeded.
Motivation:
This rule makes navigation in the code easier.
RC5.Implementation files should hold all the member function definitions for a single class (or embedded classes) as defined in the corresponding header file.
Motivation:
This rule makes navigation in the code easier.
RC6. Classes are declared with friend first (if there are any), then public, protected and private.
Motivation:
The "public" area is accessible from outside of the class, so it is this part where a user of this class need to look most often, so it is declared first. The "protected" area lies between "public" and "private" as the specification "protected" means "public" access for derived classes and "private" access for the others.
As "friend" declarations are not affected by the public, protected or private keywords, placing them at the beginning of the class seems the most natural.
RC7. Usage of global variables should be avoided: they should be encapsulated in a class. However global variables pointing to the main object of a package can occur (e.g. gAlice in AliRoot package).
Motivation:
A global variable is a variable declared outside all blocks and classes and without the static keyword, it has file scope and can have external or internal linkage. A global variable with the external linkage can be accessed and modified by any part of the program, that with the internal linkage can be accessed from all functions in the file but not from outside.
Class scope static objects provide the same scope control and better reflect the organization of our programs.
Examples:
See appendix.
RC8. The usage of friend classes should be avoided apart from I/O overloading where it is needed.
Motivation:
Friend classes break encapsulation; they are usually the symptom of a bad design.
RC9. All classes containing at least one virtual function MUST have a virtual destructor.
Motivation:
This ensures that the objects can be deleted properly.
Note:
The ROOT macro ClassDef() defines some virtual functions, so all classes using ClassDef() must have a virtual destructor.
RC10. A class that has a member datum with a type which is a built-in pointer should have a copy constructor and an assignment operator.
Motivation: Without these the default copy constructor and assignement operator would perform shallow copies and so produce two references to the same object. This is one of the easier ways to make a program crash.
Source: Atlas rules (Based on B&N p150).
Example:
class Line {
public:
Line (const Line& line); // copy constructor
Line& operator=(const Line& line); // assignment operator
...
};
RC11. Make const all member functions that are not supposed to change member data.
Motivation:
This makes the code safe from unwanted behaviour.
Example:
int GetDimension() const { return fDimension; }
RC12. Dummy argument names in member function declarations should be always provided, apart from arguments that are not used.
Motivation:
This makes the interface better to understand.
Note: To be able to provide an argument name to an unused argument for an inline function, one has to put the inline function definition outside the class defintion.
Example:
class Point{
public:
Point(Number, Number); // BAD
Point(Number x, Number y); // GOOD
NewPoint(Number /*x*/, Number /*y*/)
{printf("Not implemented\n");} // GOOD
...
};
RC13. When passing objects to a method, when they are not to be modified, pass by value if they are small, otherwise by const reference.
Motivation: Passing an object by value creates a new copy of the object , which is fine if the object is small. Passing by const reference passes the address of the object however the compiler will not allow the arguments to be modified.
Source: Atlas rules (Based on B&N p129).
RC14. All class data members should be declared as "private" or "protected".
Motivation:
This ensures that data members are only accessed from within member functions. Hiding data makes it easier to change implementation.
Source: Atlas rules (Based on B&N p89)
RC15. Structures can be used only for accessing FORTRAN common blocks, functions from external libraries, or internally within a class. They can have only data members and eventually a default constructor, member functions are not allowed. Classes should be used instead in all other cases.
Example:
struct AliPoints {
float fX;
float fY;
float fZ;
AliPoints(): fX(0), fY(0), fZ(0) {}
};
Motivation:
The difference in C++ between a class and a struct is that for a struct all data members are by default public. See motivation for rule RC14. why public access to data members shoud be avoided.
RC16. The words and phrases listed below have to be avoided in all messages and printings defined in the code. This facilitates identification of system errors in the program outputs:
- Segmentation violation
- Segmentation fault
- Bus error
- Abort
- Floating point exception
- Killed
- Busy flag cleared
RC17. Floating point numbers must not be compared with the "==" or "!=" operators , but with an appropriate function that calculates how close the two values are to each other. If the two numbers are "close enough", then we call them equal. This rule does not apply to the variables just after initialization whose value is not affected by rounding errors.
Motivation:
Direct comparison with the operators may fail due to rounding errors.
Example:
double a, b; // ... a, b get their value somewhere
NOT ALLOWED WAY
if ( a == b ){ ... }
if ( fabs( a - b ) == 0. ) { ... }
SUGGESTED WAY A
If the absolute tolerance value is known:
bool IsEqualAbs(double dX, double dY, double tolerance) {
return fabs (dX - dY) <= tolerance;
}
double const kMyTolerance = ...; // give appropriate value
if ( IsEqualAbs(a, b, kMyTolerance ) ) { ... }
SUGGESTED WAY B
If the absolute tolerance value cannot be predicted, we use can use relative comparison:
bool IsEqualRel(double dX, double dY, double epsilon) {
return fabs(dX - dY) <= epsilon * fabs(dX);
}
double const kMyEpsilon = 0.000001; // or some other small number
if ( IsEqualRel(a, b, kMyEpsilon ) ) { ... }
RC18. Data members of a class must not be redefined in derived classes.
RC19. In parameters of a method, objects that are passed by pointer or by reference and that are not modified should be declared const.
RC20. The constructors used in Root I/O should not allocate memory to data members. These constructors are either the constructors which have TRootIOCtor* as parameter type, or the default constructors. The rule does not apply to classes excluded from Root I/0 with ClassDef(ClassName,0).
Motivation:
When performing I/O, root calls the default constructor with the signature Class::Class(TRootIOCtor* =0) to initialise the storage. If you have not provided such a constructor, then the default constructor is called Class::Class(). If the called constructor allocates memory the pointers to this memory are simply overwritten during the I/O operation, unless you specify them either as reusable pointers (//->) or as transient ones (//!). This generates a leak.
RC22. The static data members of the object types should be avoided. A static access function returning a reference to the local static variable should be used instead.
Motivation:
The static data member is initialized already when loading shared libraries. If its constructor calls methods of other libraries or Root libraries it may fail due to the fact that the order of loading of libraries is arbitrary. When using a function, the object initialization is postponed until the first call to the function.
Example:
Instead of:
static Type fgMyStatic; do
static Type& MyStatic(); // in MyClass.h
Type& MyClass::MyStatic() { // in MyClass.cxx
static Type s;
return s;
}
Guidelines:
GC1. To be careful especially with:
- using exceptions
- templates - they cause problems when you create shared libraries
GC2. When only referring to pointers or references to types defined in the header file it is often not necessary to include that file. It may be sufficient to use forward declaration.
Motivation:
This saves time in compilation and avoids an apparent dependency upon the Line header file.
Source: LHCb rules.
Example:
class Line;
class Point {
public:
Number Distance(const Line& line) const;
};
GC3. Objects should always be created in a valid, ready-to-use state and should not have an 'open' function which must be called before they can be used or a 'close' functions before they are deleted.
Motivation:
Don't expect the user to call open or close functioms. The destructor should carry out all close operations.
Source: Atlas rules (based on Taligent)
GC4. Make only limited use of default arguments (or equivalent overloading with different number of arguments).
Motivation:
Using default arguments brings the risk that if you miss off an argument by mistake, you will not be told by the compiler.
Source: Atlas rules (Based on Taligent).
Style Rules
RS1. Each class contains a description of the class functionality placed at the beginning of the class header file and an extensive description at the beginning of the class implementation file.
Motivation:
As the header file contains "the C++ language description" of the class ( all declarations necessary for correct use of the class) it is natural to place the author's class description here, too.
RS2. All data members of a class are described by a short comment following the data member declaration on the same line. Motivation:
Short comments are helpful in case names of data members are not enough self-descriptive.
RS3. Member functions comments in implementation files should be put on the new line after the first "{". Motivation:
This is required by Root html generating tool.Note:
In case of inline functions implemented in the header file the comments can be put before the function declaration or can be omitted.Example:
void xxx(..) { // this is the comment line // ... ... }
RS4. Header files should not have methods bodies inside the class definitions in case they do not fit on one or two lines or when the inline function has unused arguments. These bodies of "inline" functions should go to the end of the header file after the class definition. Motivation:
This keeps interface easy to read.
RS5. There is no need to use the keyword "inline" when defining the body of a function in the class definition. Motivation:
When the body of a function is in the class definition compilers treat it as inline automatically.Example:
class Particle{ ... float GetEnergy() const { return fEnergy;}; // prefix inline redundant ... };
Automatic Support
RuleChecker Tool * NEW *
A new version of the RuleChecker has been released and at the moment this tool is being integrated into AliRoot.
The operating instructions are the following:
- The RuleChecker code can be found here.
- It should be installed in the following way:
$ wget http://aliweb.cern.ch/Offline/sites/aliweb.cern.ch.Offline/files/ruleChecker/ALICENewRuleChecker.bz2
$ mkdir -p $ALICE/local
$ cd $ALICE/local
$ tar xvfj ~/ALICENewRuleChecker.bz2
- It is not yet integrated in the framework, even if a preliminary version working with cmake is there, but there is a simple-minded script that can be used (checkAliroot):
- and again one should do:
$ wget http://aliweb.cern.ch/Offline/sites/aliweb.cern.ch.Offline/files/ruleChecker/checkAliroot
$ mkdir bin
$ mv checkAliroot bin
$ chmod +x bin/checkAliroot
- You should make sure that you have ~/bin in your path. The RuleChecker requires just the srcml package (which essentially boils down to two executables). Binary versions of the executables for mac (OS X) and Linux (Fedora) can be found here
http://www.sdml.info/projects/srcml/trunk
- It is convenient to put the two binary files somewhere in the path (/usr/local/bin for instance), either copying them there or with a link. Unfortunately there is no version for SLC4 64 bits, but Federico Carminati has managed to build one.
- For those willing to try their hands at it, the pre-requisite is to install antlr version 2, whose source can be found here:
http://www.antlr2.org/download/antlr-2.7.7.tar.gz
- A way to build the cantlr frontend has not been found, so the Makefile.antlr of srcml has to be modified to use antlr and not the self contained cantlr. The modifications are dependent on the java version you use.
Reverse Engineering Tool
It allows the visualization of the class diagram in UML format and optionally the insertion of the syntactic entities encountered during analysis into a database (assumed to be running). The insertion of the entities in the database requires to modify the file
config/config_DB
which includes information about the running database. The instructions related to the functioning of the database can be found in the file DB_README.
The script:
revEng.sh *.i
from the directory scripts, creates the dot file classDiagram.dot in the working directory and inserts the entities in the database in case all is correctly configured. The input files .i are those automatically generated in the directory check by the command make check. Otherwise they have to be created manually (see above).
Then use the script (in directory scripts):
revEngInterface.sh
to visualize it.
LIMITATIONS
- the dot tool, used to compute the layout of the class diagram, is compiled only for Linux in our current distribution
FAST EXECUTION
In this release, the user header file is not assumed to have the same name as the implementation file (with different extension). This is safer but requires more processing time. It is however possible to have a quicker execution by creating a file called "quickExecution" in the working directory. In this case it is important that the user header file (containing the implemented class definitions) be exactly one and with the same name of implementation file (with different extension). If these conditions are not satisfied the tool has to be run without "quickExecution" file or otherwise the good functioning of the tool is not granted.
DISTRIBUTION
RuleChecker and the Reverse Engineering tool are not free software. Its distribution is limited to the institutions collaborating with CERN experimental programme. To obtain a copy of RuleChecker and the Reverse Engineering tool, please follow this link.
Suggestions and bug reports can be sent to Alessandra Potrich. When submitting a bug report, please do not forget to include the following information:
- the error message (if any),
- the source code (both header and implementation files),
- the preprocessed file (.i).
Appendix
How to avoid global variables:
- Example1:
Definition of physical constants:
Instead of:extern const double gAliC; // Speed of light // AliConstants.h extern const double gAliG; // Gravitational constant // BAD // ... const double gAliC = 3.00e8; // Speed of light // AliConstants.cxx const double gAliG = 6.67e-11; // Gravitational constant // BAD // ...
One can do better:
class AliPhysicalConstants // AliConstants.h { // GOOD public: static double SpeedOfLight() const; static double GravitationalConst() const; // ... private: static const double fgC; // Speed of light static const double fgG; // Gravitational constant // ... }; const double fgC = 3.00e+08; // Speed of light // AliConstants.cxx const double fgG = 6.67e-11; // Gravitational constant // GOODdouble AliPhysicalConstants::SpeedOfLight() const
( return fgC; }double AliPhysicalConstants::GravitationalConst() const
( return fgG; }
The constants are accessible via public static methods AliPhysicalConstants::SpeedOfLight() etc.
- Example2:
If one wants to ensure that a class has only one instance and provide a global point access to it:
Instead of:class AliMaterialStore // AliMaterialStore.h { // BAD public: AliMaterialStore(); // ... } extern AliMaterialStore* gAliMaterials; AliMaterialStore::AliMaterialStore() // AliMaterialStore.cxx { // BAD gAliMaterials = this; // .. }One can do better:
class AliMaterialStore // AliMaterialStore.h { // GOOD public: static AliMaterialStore* Instance(); // .. protected: AliMaterialStore(); private: static AliMaterialStore* fgMaterials; } AliMaterialStore* AliMaterialStore::fgMaterials = 0; // AliMaterialStore.cxx // GOOD AliMaterialStore::Instance() { if (fgMaterials == 0) fgMaterials = new AlMaterialStore(); return fgMaterials; }The instance of the AliMaterialStore is accessible via AliMaterialStore::Instance() method; if it does not yet exist - this method creates it and then gives a pointer to it.
Related Web pages:
- C++ Coding Standard Specification document (from SPIDER)
- ALICE naming rules are based on Taligent's Guide to Designing Programs , see Name conventions chapter .
Information about the new RuleChecker tool was submitted by Federico Carminati Tue Dec 15 13:00:00 2009 Last modified: 16/06/2011 by Ivana Hrivnacova
