C++ coding rules

All ctools C++ code must be compatible with ISO standard C++98. Do not use C++11 features (see here if you don’t know what C++98 and C++11 is and note that C++03 is just a minor correction of C++98).

The reason for this restriction is that C++ compilers / standard libraries such as gcc / libstdc++ or clang / libc++ only finished implementing C++11 in 2013 and using C++11 features in ctools would make it impossible to use the code on many systems.

Dependencies

  • All ctools code must only depend on the ISO standard C++98 libraries and the GammaLib library.

Code lay-out

  • Each function or method starts with a curly bracket in the line following the function or method name. The return type is in the same line as the function or method name, e.g.:

    int function(void)
    {
        int i = 0;
        ...
        return i;
    }
    
  • Code blocks should be encompassed in curly brackets, even if the block consists only of a single line.

  • The opening curly bracket of a block starts in the same line as the related statement, e.g.:

    for (int i = 0; i < 10; ++i) {
        sum += i;
    }
    
  • Separate code elements by spaces, e.g.:

    Yes: int i = 0;
    
    No:  int i=0;
    
  • Align successive class definition on the member function name, e.g.:

    void        log10GeV(const double& eng);
    void        log10TeV(const double& eng);
    std::string print(void) const;
    

Naming Conventions

  • Prefix data members with m_, e.g.:

    m_num
    m_response
    m_grid_length
    m_axis_dir_qual
    

Programming Recommendations

  • Each class shall have the protected methods init_members, copy_members, and free_members to handle initialisation, copying, and eventually freeing of allocated memory for data members:

    // Initialise data members
    void Class::init_members(void)
    {
        m_elements = 10;
        m_array    = new double[m_elements];
        for (int i = 0; i < m_elements; ++i) {
            m_array[i] = 0.0;
        }
        ...
    }
    
    // Copy data members
    void Class::copy_members(const &Class class)
    {
        m_elements = class.m_elements;
        m_array    = new double[m_elements];
        for (int i = 0; i < m_elements; ++i) {
            m_array[i] = class.m_array[i];
        }
        ...
    }
    
    // Free data members
    void Class::free_members(void)
    {
        if (m_array != NULL) delete [] m_array;
        ...
    }
    
  • Each class shall have at least a void constructor, a copy constructor, a destructor and an assignment operator. Additional constructors and operators can be implemented as required. The following example shows the basic implementation for these 4 methods. Due to the usage of the init_members, copy_members, and free_members, most classes will have exactly this kind of syntax:

    // Void constructor
    ctnice::ctnice(void)
    {
        init_members();
        return;
    }
    
    // Copy constructor
    ctnice::ctnice(const ctnice& nice)
    {
        init_members();
        copy_members(nice);
        return;
    }
    
    // Destructor
    ctnice::~ctnice(void)
    {
        free_members();
        return;
    }
    
    // Assignment operator
    ctnice& ctnice::operator=(const ctnice& nice)
    {
        if (this != &nice) {
            free_members();
            init_members();
            copy_members(nice);
        }
        return *this;
    }
    
  • Do not use macros.

  • Do not use #define directives for the declaration of constants. Use const instead.

  • Do not use std::strncpy, std::memcpy or similar as these functions are corrupted on some systems.

  • If possible, pass arguments by reference.

  • Output arguments should be passed as pointers.

  • Use C++ (std::string) instead of C-style (char*) strings.

  • Use C++ casts instead of C-style casts.

  • Avoid using templates.

  • Do not use an integer for a floating point argument (i.e. write 10.0 instead of 10). Some older compilers give an error when using integers in some floating point functions, such as log10().

  • Where possible (and appropriate), use std::vector containers instead of allocating memory. In other words: avoid direct memory allocation with new.

  • Use the std:: namespace prefix where possible; write for example

    std::sin(angle);
    std::cos(angle);
    

    You may not believe it, but droping the std:: may on some systems lead to considerably slower code for trigonometric functions!

  • Use explicit for constructors with single arguments to prevent unintended type conversions. The only exception to this rule is the copy constructor or type conversion constructors.

  • Specify void for function or method definitions without arguments, e.g.:

    Yes: void function(void)
    
    No:  void function()
    
  • Use pre-incrementation in loops (pre-incrementation is faster than post-incrementation), e.g.:

    for (int i = 0; i < 10; ++i) {
        sum += i;
    }