The MIPP Coding Convention

This set of coding conventions is extracted from the MINOS C++ conventions (many thanks to Nick West who assembled that document). This document gives many reasons and examples to justify each of the rules and so it seems rather longer than it really is -- you may want to just read the summary of the rule set located at the end of the document.

Contents

  • The standard
  • Design issues
  • Summary of the rule set
  • Sources

    Document structure

    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. The list of sources is given at the end.

    The basics

    BAS-1
    The MIPP software will be based on C++. The ROOT package will be the standard for data I/O, histograms, and ntuples. The following conventions apply to code developed in C++ and are designed to be consitent with those used by ROOT.

    BAS-2
    Use of FORTRAN The MIPP offline will continue to rely on GEANT3 for the detector simulation and hence, will continue to rely on code written in FORTRAN. Also, there is potentially a large, exising code base for the MIPP detectors. Given these facts, use of "legacy" FORTRAN code will allowed. Interfaces (wrappers) between FORTRAN and C++ for this existing code will be allowed. Developers should decide on a package-by-package basis whether development of these interfaces will be more economical that re-implementing the existing FORTRAN code in C++. Development of new FORTRAN software should be avoided for use by MIPP.

    Layout on the page

    Here is a set of guidelines to writing readable C++ code.

    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)

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

    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 a short comment following the data member declaration on the same line. (Alice)

    Short comments are helpful in case names of data members are not enough self-descriptive. They are used by ROOT's HTML generator.

    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
    MIPP 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 header and implementation file should start with extensive description of the class. For example, both header and implementation files should have headers like this: //////////////////////////////////////////////////////////////////////// // $Id: index.html,v 1.1.1.1 2002/08/28 21:25:28 soltz Exp $ // // Class_name // // Class description text // ... // ... // // author@email.edu //////////////////////////////////////////////////////////////////////// The $Id: index.html,v 1.1.1.1 2002/08/28 21:25:28 soltz Exp $ tag will be filled in by cvs to record the cvs version of the file. The class name and a description of what it is and how to use it etc. should follow. Lastly, the author's e-mail address should be included so questions, comments, and bug-reports can be sent.
  • Each function declaration should be laid out like this:- //...................................................................... return_type class_name::method_name(arglist) { //====================================================================== // Function description text including description of arglist and // meaning of return_type // ... // ... //====================================================================== The dotted line separator helps when looking for a member function. THtml will use the Function description text as the material for the function summary. Important: the comments that follow the opening brace are used by THtml to define the member function description. This description is terminated by the first blank line so there should be no embedded blank lines within it.

    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.

    MIPP hasn't decided on use of a MessageService-type of package, although I would encourage us to adopt one...

    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
    Other than destructors, redeclare virtual functions as virtual in derived classes. (B&N p237)

    This is just for clarity of code. The compiler will know it is virtual, but the human reader may not. The destructor is an exception because the programmer knows that it should be virtual (see the previous rule) and by declaring it as virtual only in those classes which are not derived from any other classes makes these cases stand out.

    INH-3
    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)

    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)

    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) Source files which contain a main() function should end in ".cc".

    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 MIPP 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 MIPP 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 MIPP 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

    BAS-1
    The MIPP software will be based on C++. The ROOT package will be the standard for data I/O, histograms, and ntuples.
    BAS-2
    Use of FORTRAN Use of "legacy" FORTRAN code will allowed and interfaces (wrappers) between FORTRAN and C++ for this existing code will be allowed. Developers should decide on a package-by-package basis whether development of these interfaces will be more economical that re-implementing the existing FORTRAN code in C++. Development of new FORTRAN software should be avoided for use by MIPP.
    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 comments permited on member function declarations should be
    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
    MIPP 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 extensive description of the class following the rules of ROOT's HTML generator. Each function should have leading 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
    Other than destructors, redeclare virtual functions as virtual in derived classes. (B&N p237)
    INH-3
    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 MIPP 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)


    List of Sources

    These conventions come from many sources:
    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.


    Go Back to the The MIPP Software Top Page


    If you have any comments about this page please send them to messier@huhepl.harvard.edu