The MINOS Coding Convention

Last updated: 17 Apr 2006

If you feel strongly that further changes are necessary, contact

Nick West

See also the hot list of the most essential rules.

Contents

Abstract

Coding conventions are an essential part of OO development. Without them it is very hard to understand and maintain code. Producing a good convention requires a lot of experience in OO, experience that is not wide spread within MINOS yet. So we are proposing the model of "Minos over ALICE over ATLAS". What this means is:-
  1. We start with the ATLAS Coding Convention , developed by Steve Fisher. I should like to thank Steve Fisher, and ATLAS collaboration, for agreeing to allow us to use their convention as a starting point. Most of the material that follows, including the introduction, comes from this source.
  2. Over this we use the ALICE Coding Convention as ALICE, like MINOS, use ROOT as their framework.
  3. Finally we add, remove or change rules as dictated by the needs of MINOS.

Introduction

Why have standards?

The purpose of this document is to collect together a list of standards which should be followed when writing C++ code. It assumes that the code is hand-written and not `generated' otherwise a different set of standards would be needed for the input to the code generator.

Standards are needed for different purposes. Some things which are permissible in C++ are relics of C which most people would consider to be best avoided. Some standards relate just to the appearance (and so the readability) of the code, but have no effect upon the output of the compiler. These are here because the code has to to be reviewed and should last a long time-long after some programmers have left particle physics to get a real job.

These standards should cause no problem to new programmers, but they might be perceived as a nuisance for established C and C++ programmers. These standards were arrived at by a long process within the ATLAS-OO group. In most cases some kind of consensus was reached, but in others a vote was needed. Some of the decisions are somewhat arbitrary, however some things do not matter (e.g. which side of the road we drive on) provided that we all do it the same. The standards comprise a set of rules which are to be followed for all code which is to to be reviewed; this should include all code which has any impact upon a published physics result. None of these rules is absolute. A rule can be waived, but only by prior agreement. Some of the rules could be checked by a program but others will require a human being-a reviewer.

We imagine that these rules will be modified a little in the future, either to restrict practices which prove to be dangerous, or to allow things which had been banned. Relaxing or removing a rule has no impact upon existing code, but strengthening a rule, or adding a new rule, will upset programmers because it may invalidate existing code. For this reason we propose to start with this rather detailed set of rules.

Document structure

After this introduction comes the main body of the standards. The standard is divided into a series of sections each with its own set of numbered rules. So rule LAY-3 is rule 3 on layout. Each rule is followed by its source (in parentheses), which is one of:

B&N
Barton and Nackman, Scientific and Engineering C++, Addison-Wesley, 1994, ISBN: 0-201-53393-6
Gamma
Gamma et al., Design Patterns, Addison-Wesley, 1995, ISBN: 0-201-63361-2
CMS
A set of rules proposed by CMS
Taligent
Well-mannered object-oriented design in C++
Atlas
Rules invented by Atlas.
Alice
Rules invented by Alice.
CDF
Rules invented by CDF.
Minos
Rules invented by Minos.

Often we have changed the text, but not the the idea. In this case we have added the words `based on'. Following each rule there may be some explanation or an example or both. The rules are not written in a formal manner. So
Avoid writing FORTRAN.
and
Code adhering to the FORTRAN standard shall not be written.
mean the same.

The next section is a set of guidelines for writing good code. These are practices which we consider to be good but which cannot be defined as a clear rule.

Finally there is a summary of the rules, generated directly from the Standard.

The standard

Support tools

A set of ELisp Macros have been developed by Brett Viren that can be used in conjunction with the emacs editor help layout header and implementation files in a standard way.

Layout on the page

We will consistently use one formatting style to ensure that it is easy to read each others code-this is especially important for reviews. The layout described here is quite compact, and is also used in a number of text books, though it cannot be said to be the `best' on any objective grounds.

LAY-1
Source must not contain unprintable characters apart from those characters, appropriate to the operating system, for separating lines. In particular tabs are explicitly banned. (Minos)

Unprintable characters can cause errors in code compilation and execution which are almost impossible to diagnose. Also this ensures easy transmission over networks.

emacs tip: one can force this using the .emacs line:
| (setq-default indent-tabs-mode nil)

LAY-2
Source lines should be short and must not go beyond column 72. (Minos)

Short lines generally enhance readability. This is why newspapers use multiple columns rather than a single column 60cm wide. There is also still a need to be able to print source code, view it in editors and send it in email (sometimes quoted) without having the lines wrapped.

LAY-3
Long statements should be arranged on multiple lines in a way which maximises readability. (Atlas)

This is subjective, however a reviewer should be able to spot what is not easy to read.

LAY-4
For control structures, layout code with the brace starting a compound statement on the end of the line introducing the control structure, then indent the components of the compound statement by 2, 3 or 4 spaces (each starting on a new line) and align the closing brace (also the else, and the case and default keywords for a switch statement) with the start of the control structure. The closing brace will be on a line of its own. The spacing must be consistent within a single level of a block although nested blocks can be different if absolutely necessary. The aim is to maximise readability - more spaces help unless you start having to split lines. (MINOS)

Example of a simple for loop:

for (int i = 0; i < size; i++) {
    sum += something(i);
}
Example of an if statement:
if (x < 0) {
    y = -x;
    cout << "x was negative" << endl;
} 
else {
    y = x;
}
Example of nested ifs:
if (c == 'a') {
    cout << "It was an a" << endl;
    a++;
} 
else if (c == 'z') {
    cout << "It was a z" << endl;
    z++;
} 
else {
    cout << "It was neither a nor z" << endl;
}
The same logic with a switch statement:
switch (c) {
case 'a':
    cout << "It was an a" << endl;
    a++;
    break;
case 'z':
    cout << "It was a z" << endl;
    z++;
    break;
default:
    cout << "It was neither a nor z" << endl;
    break;
}

emacs tip: In emacs one can set the default indent in C++ mode using:

 |   ; set some stuff *after* cc-mode has started
 |   ; thereby overriding the defaults
 |   (setq c++-mode-hook
 |     '(lambda ()
 |   ; possible styles: gnu, k&4, bsd, stroustrup, whitesmith, ellemtel, linux
 |        (setq-default c-indentation-style "gnu")
 |        (setq-default c-basic-offset 3)
 |      )
 |   )

LAY-5
Use parentheses and spacing to make expressions easier to read. (B&N p30)

This is also subjective, and so relies upon the good judgement of the reviewer.

LAY-6
If a control structure (if, for, do or while) does not use a compound statement it must be coded on one line if it fits within the column maximum. (Based on B&N p32)

For example:

if (x < 0) x = -x;
Which might also have been coded:
if (x < 0) {
    x = -x;
}
but not
if (x < 0)
    x = -x;

LAY-7
Class definition bodies and function definition bodies should have their lead brace on a separate line aligned with the closing brace. Alternatively, single inline statement functions may have the enclosing braces on the same line (Minos)

Some find it harder to identify the definitions with the control structure brace convention.

For example:

class MyClass
{

public:
...
Number x() const { return m_x; }
...

protected:
...

Private:
...

};

LAY-8
Any long sequences of number should be divided into groups of 5 and start on the line after the bracket, thus:

    int array[20] = {
      27,54,78,92,12,    76,34,21,98,0,
      34,675,87,9,23,    87,23,65,78,0
    }
  instead of:
    int array[20] = { 27,54,78,
      92,12,76,34,21,98,0,34,675,87,9,
      23,87,23,65,78,
      0
    }

Header files

Name the header file after the class, using the same case and use the suffix `.h'. This is rule NAM-4

HEAD-1
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 embedded.

This rule makes navigation in the code easier.

HEAD-2
Header files should begin and end with multiple-inclusion protection, as follows:
#ifndef FILENAME_H
#define FILENAME_H
// The text of the header goes in here ...
#endif // FILENAME_H

The symbol, FILENAME_H, is constructed by taking the text which normally follows the #include where the file is included, and converting `-', `/' and `.' to `_' and all letters to capitals. (Atlas)
The Support tools can generate this automatically.

This protects the header file from multiple inclusion and circular-references.

For example the file normally included by:

#include "e-calorimeter/seed.h"

would take the form:

#ifndef E_CALORIMETER_SEED_H
#define E_CALORIMETER_SEED_H
// The text of the header goes in here ...
#endif // E_CALORIMETER_SEED_H
Note the use of the same symbol as a comment on the #endif line. This is required if the text is more than five lines as is explained later.

HEAD-3
The only trailing comments permited on member function declarations should be keywords recognised by ROOT such as *MENU*.

In ROOT, a method comment string is a part of a "ROOT meta-language". This "language" could be considered as an extension of C++. It's used to guess DataMember from comment string, see TMethod:FindDataMember, - to declare the method as a context-menu item. The list of active words is "*TOGGLE", "*MENU", "*SUBMENU"

Currently this is a short list, but it could be extended in the future and it is dangerous to mix it with other comments. (Minos)

HEAD-4
Include dummy argument names in function declarations unless the meaning is clear without them. (Atlas)

The example:

class Point 
{

public:
    Point(Number x, Number y);          
    Number distance(Point point) const; 
    Number distance(Line line) const;  

};
is easier to understand than:
class Point 
{

public:
    Point(Number, Number);        
    Number distance(Point) const; 
    Number distance(Line)  const; 

};

HEAD-5
Data members of a class should be declared private or protected. (Based on B&N p89)

This ensures that data members are only accessed from within member functions. Hiding data makes it easier to change implementation and provides a more uniform interface to the object.

For example:

class Point 
{

public:
    Number x() const; // Return the x coordinate
private:
    Number m_x;       // The x coordinate (safely hidden)

};
The fact that the class Point has a data member m_x which holds the x coordinate is hidden.

HEAD-6
Header files should not have methods bodies inside the class definitions that do not fit on one or two lines.

HEAD-7
Use inline functions only to return values that are simple expressions of private member data. There is no need to declare them inline; this is implicit for function definitions in the class statement. (based on B&N p55)

For example:

class Point 
{

public:
    Number x() const { return m_x; } // Return the x coordinate
private
    Number m_x;

};

HEAD-8
Classes are declared with friends first then public, protected and private. Within each section nested types (e.g. enum or class) should appear at the top. Within each section members are ordered as follows:-
  1. Constructors.
  2. Destructors.
  3. Member functions that do not change the object's state i.e. func (...) const;
  4. Member functions that do change the object's state.
  5. Data members
Within each group, members are in strict alphabetical order. (Minos)

For example:

class Path 
{

    friend Path & operator + (const Path &, const Path &);

public:
    Path();
    ~Path();

protected:
    void draw();

private:
    class Internal { 
       // Path::Internal declarations go here ...
    };

};
As the default at the beginning of a class is private, this means that friend declarations appear within a private section. However friend declarations are unaffected by the private, protected and public keywords, so placing them at the beginning seems most natural.

An exception to the ordering is the placement of the members generated by the ClassDef macro; this macro must be placed at the very end of the class definition i.e. just before the closing }. However since these member are not visible to the reader of the header, this trangession is also invisible!

HEAD-9
Global functions should only be used for symmetric binary operators and where a global function seems more appropriate than using an object e.g. mathematical functions. (Based on Taligent)

HEAD-10
Each class contains a summary of the class functionality placed at the beginning of the class header file. This should be sufficient for the reader to determine whether the class is fit for a particular purpose and may briefly refer to inherited functionality that would typically be used directly by clients of the class. (Minos)

A full description should be placed in the implementation file, see rule IMP-4

HEAD-11
All data members of a class are described by an associated short comment.

Short comments are helpful in case names of data members are not enough self-descriptive. They should conform to Doxygen's convention on comments"

Implementation files

Name the implementation file after the class, using the same case using the suffix `.cxx'. This is rule NAM-5.

IMP-1
Generally a file will hold all the member function definitions for a single class. If one class is embedded within another the member function definitions would be expected to share a file. The organisation should be consistent with the header file. see HEAD-2. (Based on CMS)

IMP-2
MINOS defined header files should be included with a statement of the form #include "directory_name/file_name.h". Normally at least one level of directory should be specified. (Atlas)

IMP-3
Use forward declarations instead of including a header file if this is sufficient. (Atlas)

For example:

class Line;

class Point 
{

public:
    Number distance(Line line) const;  // Distance from a line

};
Here it is sufficient to say that Line is a class, without giving details which are inside its header. This saves time in compilation and avoids an apparent dependency upon the Line header file. It also avoiding polluting the global name space which is very important for long-lived project

IMP-4
The implementation file should start with brief description of the class. Class member function comments should follow Doxygen's convention. Code blocks should also be commented. Here they should be kept short and informative, and not echo the code.(Minos)

IMP-5
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. (Alice)

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 access 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.

IMP-6
Singletons are banned except as part of the framework. (CDF)

The object of any class that must, by design, only have a single instantiation, is called a singleton. A program that consists of a single object is procedural, not OO, so any class that can only produce a single object should rightly be regarded with suspicion. Experience shows that singletons are both attractive and dangerous as a way of short-curcuiting a full OO design. Their existence in the framework can be justified where they manage some resource e.g. the database interface; if the resource is unique then it is reasonable that a unique object manages it.

IMP-7
The usage of friend classes should be avoided if possible apart from operator overloading (particularly I/O overloading) where it is needed. (Alice)

Friend classes break encapsulation; they are usually the symptom of a bad design. If an object has a public face (for its clients) and a private face (for classes that cooperate with it to serve those clients) this can be achieved using Abstract Base Classes, which should reduce the need for friends.

IMP-8
Don't use cout and cerr (stdout, stderr); all output must be sent to the MessageService package.

IMP-9
Handle bad pre- and post- conditions. Where an object is sent invalid data (bad pre-condition) or is returned bad data (bad post-condtion) and where this would, if untrapped, cause a job, it must be handled. The most typical example would be getting a null pointer in cases where it cannot be null and an acceptible way of handling is to use the assert macro.

Often it is not possible for the object to sensibly recover from some pre- or post- condition violation. This is particulary true in an OO program. Eventually the error handling throw ... catch may make it possible to do some event level recovery but such systems are not yet solid over a full range of platforms. Simply to do nothing and wait for the resulting segmentation violation is not acceptible as it gives no clue as to the error.

Argument passing

ARG-1
When passing objects to a method, when they are not to be modified, pass by value if they are small, otherwise by constant reference. (Based on B&N p129)

Passing an object by value creates a new copy of the object, which is fine if the object is small. Passing by constant reference passes the address of the object however the compiler will not allow the arguments to be modified.

For example:

class Point 
{

public:
    Number distance(Point point) const;       // Distance to a point
    Number distance(const Line & line) const; // Distance from a line

};
Here a Line may be large so it is passed by constant reference.

ARG-2
Always qualify unmodifiable arguments by using the const modifier in the declaration of the formal arguments. (B&N p125)

By implication, the omission of the const modifier implies that the argument may be modified. If the previous example had been written:

class Point 
{

public:
    Number distance(Point point) const; // Distance to a point
    Number distance(Line & line) const; // Distance from a line

};
the member function, Number distance(Line &) const, would be allowed to modify the Line passed to it.

ARG-3
Declare as const those member functions that are not meant to alter member data. (The mutable keyword should be used where appropriate.) (B&N p145)

For example:

class Point 
{

public:
    Number distance(const Line & line) const; // Distance from line
    void translate(const Vector & vector);    // Shift a point

};
here we see that distance is const; it does not change the state of Point. Conversely translate is not declared const, because it does.

Declaring member functions to be const where they are not meant to modify member data makes the code safer, and is useful in understanding what a class is supposed to do.

ARG-4
Make only limited use of default arguments (or the equivalent overloading with different number of arguments). (Taligent)

This is rather subjective, so the reviewer must decide if the use made of this feature is worth the risk. The risk is that if you miss off an argument by mistake, you will not be told by the compiler.

For example with:

class Point 
{

public:
    Point(Number x, Number y = 0);          // Create from (x,y)

};
then
    Point p(10);
would be valid and equivalent to:
    Point p(10, 0);
This might be what you want to happen, or it might be a typing error.

ARG-5
Do not declare functions to have unspecified arguments (i.e. avoid using `...'). (Taligent)

The compiler cannot check unspecified arguments. This facility was used by the C library function, printf. Now cout should be used instead.

ARG-6
Use references rather than pointers as arguments whenever possible. (Based on Taligent)

References are more convenient than pointers, however sometimes a pointer must be used for example when the function you call retains a reference (an alias) to the object you are passing in, such as when you construct a dynamic data structure.

Another example of an unavoidable pointer argument are C functions such as scanf that return results via their arguments, for example:-


  scanf("%d", &num);
although this example infringes rule DANG-1.

ARG-7
If a function returns a value and has side effects (such as modifying its input parameters), then this should be apparent from the choice of name. (Atlas)

Even with the systematic use of the const qualifier, it is still worth designing functions which are free of side effects.

ARG-8
Dummy argument names in member function declarations should be always provided. (Alice)

This makes the interface better to understand.

Initialisation

INIT-1
Declare each variable with the smallest possible scope and initialise it at the same time. (Based on B&N p39)

For example:

  float  f(1.23F);
  double d(1.23E-308);

Warning: Only globals and class static variables of arithmetic or pointer type are initialized to 0 by default; all others, including pointers, will have junk!! Always initialise your variables!!

Of course it is perfectly fine to use default constructors for objects e.g.

  Number n;
because any well written class would ensure all its constructors left objects properly and fully initialised (see INIT-3).

INIT-2
Use one declaration statement for each variable. (B&N p39)

For example:

Number n(0);     // a number used for ...
Point p(20,10);  // a point ...
This example also shows a trailing comment for saying what each variable is.

INIT-3
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. (Based on Taligent)

Don't expect the user to call open or close functions. The destructor should carry out all close operations.

INIT-4
Do not use file scope objects; use class scope instead. (Based on B&N p179)

File scope is a complication we can do without.

INIT-5
List base initialisers in the order in which the bases appear in the class definition and before member initialisers. (B&N p269)

This is because the order of the initialisers (when they are made explicit in the constructor definition) is ignored. So it is best to arrange them in the order in which they will be executed, which is base classes in the order in which the bases appear in the class definition followed by members in the order in which they are declared.

INIT-6
Member initialisers should appear in the order in which the members are declared. (B&N p147)

See the explanation with the previous rule.

Constructors, destructors and memory management

CTOR-1
Single argument non-converting constructors should use the explicit keyword. (Based on B&N p155)

This is to avoid possible confusing behaviour with assignments. A non-converting constructor constructs objects just as converting constructors, but does so only where a constructor call is explicitly indicated by the syntax.

For example with the definition of the constructors for Z:

class Z 
{

public:
    explicit Z(int);
    // ...

};
the following assignment would be illegal:
    Z a1 = 1;
however
    Z a1(1);
would work, as would:
    Z a1 = Z(1);
    Z* p = new Z(1);

CTOR-2
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. (Based on B&N p150)

For example

class Line 
{

public:
    Line (const Line &);              // copy constructor
    Line & operator= (const Line &);  // assignment operator
    // ...

};

Without these the default copy constructor and assignment 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.

If you do not think it likely that anyone will want to copy an object of the class you are working on, then just provide private declarations of the copy constructor and assignment operator then the object will be uncopyable.

CTOR-3
The argument to a copy constructor and to an assignment operator should be a constant reference. (Base on B&N p150 and p164)

This ensures that the object being copied is not altered by the copy or assign.

See the previous example.

CTOR-4
To behave as expected, the assignment operator functions should return a reference to their left operand. (B&N p164 and Taligent)

This ensures that

a = b = c;
will assign c to b and then b to a as is the case with built in objects.

CTOR-5
Assignment member functions should work correctly when the left and right operands are the same object. (B&N p164)

This requires some care when writing assignment code, as the case when left and right operands are the same may require that most of the code is bypassed.

CTOR-6
Match every invocation of new with exactly one invocation of delete. (B&N p190)

This is a minimum requirement to keep control of memory.

CTOR-7
Any pointers to automatic objects must have the same, or a smaller scope than the object they point to. (Based on B&N p193)

This makes sure that when the object goes out of scope and is destroyed, the pointer is not still trying to point to it.

CTOR-8
A function must not use the delete operator on any pointer passed to it as an argument. (Based on B&N p193).

This is also to avoid dangling pointers, i.e. pointers to memory which has been given back. Such code will often continue to work until the memory is re-allocated for another object.

User defined operators

UOP-1
Give operators conventional definitions. (B&N p161)

For example if you define the operator +, then it should do something like addition.

UOP-2
Declare symmetric binary operators as global functions. (B&N p162)

This is the only way to get conversions of the left operand of binary operations to work. It is common in implementing the symmetric operator to call the corresponding asymmetric binary operator. The problem comes with situations like:-


Complex a,b;
a = b * 3.0;
if there is a converting constructor to convert 3.0 to Complex then this will work if there is a member operator* function - the compiler tries b.operator*(Complex(3.0)). However:-

a = 3.0 * b;
fails because (3.0).operator*(b) makes no sense. With a global function it will try operator*(Complex(3.0),b) and all is well. Note however, that converting constructors are banned by CTOR-1.

UOP-3
Define asymmetric binary operators and unary operators that modify their operand as member functions. (B&N p162)

Inheritance

INH-1
Declare a destructor, which is virtual, in every base class (i.e. one having at least one virtual function) unless it is derived from another base class that provides a virtual destructor. (Based on B&N p234)

This ensures that objects can be properly deleted when referenced by base class pointers.

INH-2
Avoid multiple inheritance from all but pure Abstract Base Classes if it is possible

(Minos)

Names of things

General Rules

NAM-1
Names should be chosen with care and should be meaningful (Atlas). Generic names such as Event, MonteCarlo, Reconstruction and Analysis must not be used as they are too general to apply to specific components of our software (Minos).

NAM-2
In names that consist of more then one word: (Alice)

NAM-3
No special characters in names are allowed (_,#,$,&,@,-,%).((Alice) Examples:
The name that consists of words alpha, beta, gamma can only be:
AlphaBetaGamma or alphaBetaGamma)

Headers and Implementation Files

NAM-4
Header file names are derived from the class name and have the suffix ".h" (Classname.h). (Alas)

NAM-5
Implementation file names are derived from the class name and have the suffix ".cxx" (Classname.cxx). (Atlas)

Class Names

NAM-6
Class names start with the prefix of the internal package to which they belong. (Minos)

Member Function Names

NAM-7
Member function names start with a capital (Alice)

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".

NAM-8
The same name must not be shared between member functions that do and do not change an object's state.(Minos)

It would be very dangerous to use such a member function. A user many only mean to interrogate the object but could, be using the wrong calling sequence, end up changing it. In any case, overloading should only be used to group together logically equivalent operations and manifestly interrogation and modification are fundementally different.

Data Member Names

NAM-9
Data member names start with a prefix "f" (Alice)

Local Variable Names

NAM-10
Local variables names start with a lower case letter. (Alice)

Global Variable Names

NAM-11
Global variables names start with a prefix "gXxx" where Xxx denotes the package prefix. (Minos)

Constants

NAM-12
Constants, including constant statics and enumeration type values start with a prefix "k" (Alice). Enumeration type names should start E (ROOT,Taligent)

Examples:
const float kKineticEnergy;
enum EColor { kRed, kGreen, kBlue };


Public enums should be included to global scope by adding to LinkDef.h e.g.


#pragma link C++ enum   EColor;
This will make them available during an interactive CINT session.

Static Data Member Names

NAM-13
Static data members names start with a prefix "fg". (Alice)

File Name Usage

NAM-14
No file names are allowed to be "hard wired" into the code. Ideally they should be accessed via a service similar to LHCb's Job Option service , or failing that they should be placed in one class as static data members with static public get methods. (Minos)

Hardwired names are clearly unacceptible, but the natural alternative, environmental variables, can quickly become unmanagable. So MINOS will develop something along the lines of LHCb's Job Option service so that all this information is managed at a single point which provides a primary point of control to the job user. Until this service is available, the use of environmental variables is acceptible, but be prepared to rework code that accesses them.

NAM-15
Do not use plural in names. This avoids worrying about whether something is called e.g. muTrackHit or muTrackHits, its called muTrackHit!

Use of the pre-processor

The general message of this section is only to use the preprocessor for doing things which cannot be done by other means.

PREP-1
Do not use #define to define symbolic constants. (Based on B&N p76)

Use const for individual constants or enum for enumeration.

PREP-2
Use templates for parameterised types rather than the pre-processor. (Based on Taligent)

PREP-3
All #else and #endif directives should carry a comment that tells what the corresponding #if was about if the conditional section is longer than five lines. (Atlas)

For example:

#ifndef GEOMETRY_POINT_H
#define GEOMETRY_POINT_H
class Point 
{

public:
    Point(Number x, Number y);                // Create from (x,y) 
    Number distance(Point point) const;       // Distance to a point
    Number distance(const Line & line) const; // Distance from a line
    void translate(const Vector & vector);    // Shift a point

};
#endif // GEOMETRY_POINT_H

PREP-4
Use functions (maybe inline) rather than preprocessor macros. (Atlas)

Portability issues

PORT-1
All code must follow the C++ standard. Additional restrictions may be adopted to make sure that the code runs on all supported platforms. (Alice)

Following this rule ensures portability for different platforms.

PORT-2
The MINOS code should compile without any warnings under all supported compilers. Only warnings brought from external packages can occur. (Alice)

Compiler warnings usually point out to imperfections that can be eliminated.

PORT-3
Use ROOT typedefs for the C++ built in types. (Atlas/Minos)

This is for portability reasons, to give consistent numerical results and for compatibility with persistence schemes.

Control structures

CTL-1
The global function called main (which is the start of the program) should return either EXIT_SUCCESS or EXIT_FAILURE and no other value. (Atlas)

For example:

#include <cstdlib>

main () 
{
     bool success(true);

    // Do something which might clear success

     return success? EXIT_SUCCESS : EXIT_FAILURE;
}

CTL-2
The exception mechanism should be used to deal with `unusual' circumstances rather than exploiting error codes. (Based on Taligent)

CTL-3
Don't use goto. (Taligent)

CTL-4
In switch statements each choice must have a closing break, or be on the same line as another choice or there should be a comment to indicate that the fall-through is the desired action. (Atlas)

For example

switch (expression) {
case constant:
    statements;
    break;
default:
    statements;
    break;
}

Dangerous or superfluous constructs

DANG-1
Use the iostream.h functions rather than those defined in stdio.h (Based on B&N p 72 & p542)

For example a file with the header:

#include <iostream.h>
might have:
    Number n;
    cin >> n;
    cout << "Value entered was " << n << endl;

DANG-2
Use the integer constant 0 for the null pointer (B&N p76)

In C it was normal to use the macro NULL. This might be for example ((void *) 0).

DANG-3
Use new and delete instead of malloc() and free() (B&N p78)

DANG-4
Unless you need more code on the same source line use `// xxx' rather than `/* xxx */' for comments (Based on CMS rule)

DANG-5
The keyword struct is deprecated. It must not be used in the class interface and should only be used internally under exceptional circumstances. (Minos)

The struct is identical to the class except that by default its contents are public rather than private. As such it violates rule HEAD-5. By definition a struct represents a concept that is more global than its components and some processes will act directly on this concept. These processes should be bound to it i.e. as member functions rather than explicitly assuming a particular representation. This is particularly true of the interface; passing structs starts to expose an object's implimentation to its clients.

DANG-6
Use the bool type of C++ for booleans. (Based on Taligent)

C programmers may tend to use int instead of bool as that was all they had.

DANG-7
Use the STL string (preferred) or ROOT TString class rather than const char* where possible. (Minos)

DANG-8
Do not use C style type casts of the form (Type)variable, nor the keyword reinterpret_cast, and only use const_cast when obliged to do so when calling code which has not used the const keyword correctly. (Atlas)

The dynamic_cast is safe as it make use of run time type information. If you have to make much use of the dynamic_cast, this could be a sign of poor design.

DANG-9
Do not use union types. (Atlas)

Unions offer a further means to shoot yourself in the foot. A union is similar to a class but all the data members have the same address. If you are a C programmer and are tempted to use a union think about polymorphism instead.

DANG-10
Use enum for related constants rather than const. (Taligent)

The enum construct allows a new type to be defined and hides the numerical values of the enumeration constants.

For example:

    enum State {halted, starting, running, halting, paused};

DANG-11
The use built-in arrays `[]' can only be used if the code is protected against out of bounds access. (Minos)

The built in arrays offer no protection from falling off the end, and they are rarely the most suitable container. There are situations, for example translation tables, where arrays are perfectly acceptible. However, as soon as arrays hold data passed to an object by its clients there is a risk that the arrays will overflow. For example, suppose the object will hold a file name that will be passed to it as character string, how big an array is really "safe"?

The minimal level of protection in such dynamic circumstances is as follows:-

  1. Define the size of the array with an enum defined within the class header.
  2. Check any array index that could exceed array size and if out of range:-
    1. Report the error and the remedy i.e. what enum must be changed
    2. Abort the operation if it makes sense to do so or the program if not.
Demanding this level care, which is still unsatisfactory in that the program may fail at time, is a compelling argument in favour of containers. Choose one of the STL container (preferred) or ROOT classes.

For arrays of primitive data types ROOT offers a number of simple 1 dimensional arrays, for example TArrayF, an array of floats. These can be handled in a very intuitive way, for example:-


// Create an array of 10 floats

  TArrayF my_floats{10);  
 
// Use them

  my_floats[0] = 0.;
  for ( Int_t i=1; i<10, i++ ) my_floats[i] = my_floats[i-1] + 1;
For higher dimensional arrays, a standard library like CLHEP can be used.

DANG-12
Use C++ style casts, not old C style casts. (Minos)

Don't use the C style cast e.g.:-

    float f = 3.14159;
    int   i = 10 * (int) f;
Instead choose the appropriate C++ style:-
  1. static_cast<T>(expression) e.g. static_cast<int>(f)
    to replace the general C style cast, typically converting between basic types.
  2. dynamic_cast<T>(expression) e.g. dynamic_cast<Muon*>(trackPtr)
    to move up or across an inheritance tree. Illegal moves e.g. moving to sub-class when only have base class object will return 0. The cast can also be used to convert references e.g. dynamic_cast<Muon&>(*trackPtr) but in this case an illegal move causes an exception as a reference has to bind to an existing object. Note that:-
    • The class must have at least one virtual function (the compiler uses the pointer to the virtual function table to check the class).
    • No casting is required when converting a pointer or reference from a sub-class to a base class it inherits from; such conversions are built into the language.
  3. const_class<T>(expression) e.g. const_cast<char *>("A string")
    to "cast away const". Now that "A string" is actually a char[] that can be converted safely to const char*. Casting away means that the string could be overwritten which could well be fatal. So treat with extreme caution, in general the only time this is O.K., is when passing arguments to 3rd party code that has been sloppy with const i.e. has not declared them const even though they are not modified.
  4. reinterpret_cast<T>(expression) e.g. reinterpret_cast<void*>(100)
    to completely reinterpret the value. By far the most dangerous cast, there has to be a very good reason for using it!
The C++ style casts are better because:- Example:- class base { virtual ~base(); // need at least one virtual method for // RTTI to work prope }; class derived : public base { virtual ~derived(); }; class another { virtual ~another(); } void func1(base& b) { derived& ptr1 = dynamic_cast<derived&>(b); // works fine another& ptr2 = dynamic_cast<another&>(b); // throws an exception } void func2(base* b) { derived* ptr1 = dynamic_cast<derived*>(b); // works fine another* ptr2 = dynamic_cast<another*>(b); // return 0 in ptr // - no exception } int main() { derived a; base* b = &a; // automatic - no cast needed from derived to base func1(a); // automatically converts to base& func2(b); } [End of file]

Design issues

Beside sets of coding rules, both ATLAS and BABAR have produced lists of good programming practices. Perhaps MINOS should merge these is a single list for our use?

ATLAS Design Issues

B&N promote the separation of interface and implementation. They define an interface base class as a base class used to specify virtual functions that are defined in derived classes and called through pointers or references to the base class. Generally an interface base class has no data members and all its member functions are virtual.

BABAR Reconstruction Design Checklist

These are not reproduced here, see:- http://www.slac.stanford.edu/BFROOT/www/Computing/Offline/Reconstruction/CompRecCheck.html

Summary of the rule set

LAY-1
Source must not contain unprintable characters apart from those characters, appropriate to the operating system, for separating lines. In particular tabs are explicitly banned. (Minos)
LAY-2
Source lines should be short and must not go beyond column 72. (Minos)
LAY-3
Long statements should be arranged on multiple lines in a way which maximises readability. (Atlas)
LAY-4
For control structures, layout code with the brace starting a compound statement on the end of the line introducing the control structure, then indent the components of the compound statement by 2, 3 or 4 spaces (each starting on a new line) and align the closing brace (also the else, and the case and default keywords for a switch statement) with the start of the control structure. The closing brace will be on a line of its own. The spacing must be consistent within a single level of a block although nested blocks can be different if absolutely necessary. The aim is to maximise readability - more spaces help unless you start having to split lines. (MINOS)
LAY-5
Use parentheses and spacing to make expressions easier to read. (B&N p30)
LAY-6
If a control structure (if, for, do or while) does not use a compound statement it must be coded on one line if it fits within the column maximum. (Based on B&N p32)
LAY-7
Class definition bodies and function definition bodies should have their lead brace on a separate line aligned with the closing brace. Alternatively, single inline statement functions may have the enclosing braces on the same line (Minos)
LAY-8
Any long sequences of number should be divided into groups of 5 and start on the line after the bracket, thus:

    int array[20] = {
      27,54,78,92,12,    76,34,21,98,0,
      34,675,87,9,23,    87,23,65,78,0
    }
  instead of:
    int array[20] = { 27,54,78,
      92,12,76,34,21,98,0,34,675,87,9,
      23,87,23,65,78,
      0
    }
HEAD-1
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 embedded.
HEAD-2
Header files should begin and end with multiple-inclusion protection, as follows:
#ifndef FILENAME_H
#define FILENAME_H
// The text of the header goes in here ...
#endif // FILENAME_H
HEAD-3
The only trailing comments permited on member function declarations should be keywords recognised by ROOT such as *MENU*.
HEAD-4
Include dummy argument names in function declarations unless the meaning is clear without them. (Atlas)
HEAD-5
Data members of a class should be declared private or protected. (Based on B&N p89)
HEAD-6
Header files should not have methods bodies inside the class definitions that do not fit on one or two lines.
HEAD-7
Use inline functions only to return values that are simple expressions of private member data. There is no need to declare them inline; this is implicit for function definitions in the class statement. (based on B&N p55)
HEAD-8
Classes are declared with friends first then public, protected and private. Within each section nested types (e.g. enum or class) should appear at the top. Within each section members are ordered as follows:-
  1. Constructors.
  2. Destructors.
  3. Member functions that do not change the object's state i.e. func (...) const;
  4. Member functions that do change the object's state.
  5. Data members
Within each group, members are in strict alphabetical order. (Minos)
HEAD-9
Global functions should only be used for symmetric binary operators and where a global function seems more appropriate than using an object e.g. mathematical functions. (Based on Taligent)
HEAD-10
Each class contains a summary of the class functionality placed at the beginning of the class header file. This should be sufficient for the reader to determine whether the class is fit for a particular purpose and may briefly refer to inherited functionality that would typically be used directly by clients of the class. (Minos)
HEAD-11
All data members of a class are described by a short comment following the data member declaration on the same line. (Alice)
IMP-1
Generally a file will hold all the member function definitions for a single class. If one class is embedded within another the member function definitions would be expected to share a file. The organisation should be consistent with the header file. see HEAD-2. (Based on CMS)
IMP-2
MINOS defined header files should be included with a statement of the form #include "directory_name/file_name.h". Normally at least one level of directory should be specified. (Atlas)
IMP-3
Use forward declarations instead of including a header file if this is sufficient. (Atlas)
IMP-4
The implementation file should start with brief description of the class. Class member functions should follow Doxygen's convention on comments". Code blocks should also be commented. Here they should be kept short and informative, and not echo the code. The class description and each member function must have the headings Purpose, Arguments, Return, Contact. (Minos)
IMP-5
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. (Alice)
IMP-6
Singletons are banned except as part of the framework. (CDF)
IMP-7
The usage of friend classes should be avoided if possible apart from operator overloading (particularly I/O overloading) where it is needed. (Alice)
IMP-8
Don't use cout and cerr (stdout, stderr) all output will be sent to a message handler similar to LHCb's Message service.
IMP-9
Handle bad pre- and post- conditions. Where an object is sent invalid data (bad pre-condition) or is returned bad data (bad post-condtion) and where this would, if untrapped, cause a job, it must be handled. The most typical example would be getting a null pointer in cases where it cannot
ARG-1
When passing objects to a method, when they are not to be modified, pass by value if they are small, otherwise by constant reference. (Based on B&N p129)
ARG-2
Always qualify unmodifiable arguments by using the const modifier in the declaration of the formal arguments. (B&N p125)
ARG-3
Declare as const those member functions that are not meant to alter member data. (The mutable keyword should be used where appropriate.) (B&N p145)
ARG-4
Make only limited use of default arguments (or the equivalent overloading with different number of arguments). (Taligent)
ARG-5
Do not declare functions to have unspecified arguments (i.e. avoid using `...'). (Taligent)
ARG-6
Use references rather than pointers as arguments whenever possible. (Based on Taligent)
ARG-7
If a function returns a value and has side effects (such as modifying its input parameters), then this should be apparent from the choice of name. (Atlas)
ARG-8
Dummy argument names in member function declarations should be always provided. (Alice)
INIT-1
Declare each variable with the smallest possible scope and initialise it at the same time. (Based on B&N p39)
INIT-2
Use one declaration statement for each variable. (B&N p39)
INIT-3
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. (Based on Taligent)
INIT-4
Do not use file scope objects; use class scope instead. (Based on B&N p179)
INIT-5
List base initialisers in the order in which the bases appear in the class definition and before member initialisers. (B&N p269)
INIT-6
Member initialisers should appear in the order in which the members are declared. (B&N p147)
CTOR-1
Single argument non-converting constructors should use the explicit keyword. (Based on B&N p155)
CTOR-2
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. (Based on B&N p150)
CTOR-3
The argument to a copy constructor and to an assignment operator should be a constant reference. (Base on B&N p150 and p164)
CTOR-4
To behave as expected, the assignment operator functions should return a reference to their left operand. (B&N p164 and Taligent)
CTOR-5
Assignment member functions should work correctly when the left and right operands are the same object. (B&N p164)
CTOR-6
Match every invocation of new with exactly one invocation of delete. (B&N p190)
CTOR-7
Any pointers to automatic objects must have the same, or a smaller scope than the object they point to. (Based on B&N p193)
CTOR-8
A function must not use the delete operator on any pointer passed to it as an argument. (Based on B&N p193).
UOP-1
Give operators conventional definitions. (B&N p161)
UOP-2
Declare symmetric binary operators as global functions. (B&N p162)
UOP-3
Define asymmetric binary operators and unary operators that modify their operand as member functions. (B&N p162)
INH-1
Declare a destructor, which is virtual, in every base class (i.e. one having at least one virtual function) unless it is derived from another base class that provides a virtual destructor. (Based on B&N p234)
INH-2
Avoid multiple inheritance from all but pure Abstract Base Classes if it is possible
NAM-1
Names should be chosen with care and should be meaningful. (Atlas)
NAM-2
In names that consist of more then 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.
(Alice)
NAM-3
No special characters in names are allowed (_,#,$,&,@,-,%).((Alice) Examples:
The name that consists of words alpha, beta, gamma can only be:
AlphaBetaGamma or alphaBetaGamma)

NAM-4
Header file names are derived from the class name and have the suffix ".h" (Classname.h). (Alas)
NAM-5
Implementation file names are derived from the class name and have the suffix ".cxx" (Classname.cxx). (Atlas)
NAM-6
Class names start with the prefix of the internal package to which they belong. (Minos)
NAM-7
Member function names start with a capital (Alice)
NAM-8
The same name must not be shared between member functions that do and do not change an object's state.(Minos)
NAM-9
Data member names start with a prefix "f" (Alice)
NAM-10
Local variables names start with a lower case letter. (Alice)
NAM-11
Global variables names start with a prefix "gXxx" where Xxx denotes the package prefix. (Minos)
NAM-12
Constants, including constant statics and enumeration type values start with a prefix "k" (Alice). Enumeration type names should start E (ROOT,Taligent)
NAM-13
Static data members names start with a prefix "fg". (Alice)
NAM-14
No file names are allowed to be "hard wired" into the code. Ideally they should be accessed via a service similar to LHCb's Job Option service , or failing that they should be placed in one class as static data members with static public get methods. (Minos)
NAM-15
Do not use plural in names. This avoids worrying about whether something is called e.g. muTrackHit or muTrackHits, its called muTrackHit!
PREP-1
Do not use #define to define symbolic constants. (Based on B&N p76)
PREP-2
Use templates for parameterised types rather than the pre-processor. (Based on Taligent)
PREP-3
All #else and #endif directives should carry a comment that tells what the corresponding #if was about if the conditional section is longer than five lines. (Atlas)
PREP-4
Use functions (maybe inline) rather than preprocessor macros. (Atlas)
PORT-1
All code must follow the C++ standard. Additional restrictions may be adopted to make sure that the code runs on all supported platforms. (Alice)
PORT-2
The MINOS code should compile without any warnings under all supported compilers. Only warnings brought from external packages can occur. (Alice)
PORT-3
Use ROOT typedefs for the C++ built in types. (Atlas/Minos)
CTL-1
The global function called main (which is the start of the program) should return either EXIT_SUCCESS or EXIT_FAILURE and no other value. (Atlas)
CTL-2
The exception mechanism should be used to deal with `unusual' circumstances rather than exploiting error codes. (Based on Taligent)
CTL-3
Don't use goto. (Taligent)
CTL-4
In switch statements each choice must have a closing break, or be on the same line as another choice or there should be a comment to indicate that the fall-through is the desired action. (Atlas)
DANG-1
Use the iostream.h functions rather than those defined in stdio.h (Based on B&N p 72 & p542)
DANG-2
Use the integer constant 0 for the null pointer (B&N p76)
DANG-3
Use new and delete instead of malloc() and free() (B&N p78)
DANG-4
Unless you need more code on the same source line use `// xxx' rather than `/* xxx */' for comments (Based on CMS rule)
DANG-5
The keyword struct is deprecated. It must not be used in the class interface and should only be used internally under exceptional circumstances. (Minos)
DANG-6
Use the bool type of C++ for booleans. (Based on Taligent)
DANG-7
Use the STL string (preferred) or ROOT TString class rather than const char* where possible. (Minos)
DANG-8
Do not use C style type casts of the form (Type)variable, nor the keyword reinterpret_cast, and only use const_cast when obliged to do so when calling code which has not used the const keyword correctly. (Atlas)
DANG-9
Do not use union types. (Atlas)
DANG-10
Use enum for related constants rather than const. (Taligent)
DANG-11
The use built-in arrays `[]' can only be used if the code is protected against out of bounds access. (Minos)


Go Back to the The OO Companion Top Page


If you have any comments about this page please send them to Nick West