Skip to content

Coding Guidelines

Andreas Nicolai edited this page Dec 4, 2018 · 3 revisions

Coding Guidelines

File and Namespace Naming Conventions

Library code should be placed in namespaces. Also, all files should be prefixed with a corresponding file name prefix. For example:

Library/Module Class Name Example Namespace Header file Implementation file Header Include Guard
IBK (regular classes) ArgParser IBK IBK_ArgParser.h IBK_ArgParser.cpp IBK_ArgParserH
IBK (templates) matrix_view<> IBK IBK_matrix_view.h IBK_matrix_view.cpp IBK_matrix_viewH
IBK bitfield IBK IBK_bitfield.h IBK_bitfield.cpp IBK_bitfieldH

AN: The _ notation for templates is used to have similar appearance as templates from the C++ standard library, for example, std::back_inserter().

General Conventions

Generally, a clean and concise coding style should be used, for example, place your opening and closing braces like in the following example:

    // Short function declarations may have the opening brace in the same line 
    void someFunction(int t) {
        // indent with one tab character
        for (int i=0; i<t; ++i) { // also place the opening brace in this line
            // code
        }
        // longer for-clauses with more than one line should place
        // the opening brace into the next line to mark the opening scope.
        for (std::vector<double>::iterator it = m_localVec.begin();
            it != m_localVec.end(); ++it)
        {
            // code
        }
        // similar rules apply for if and other clauses, for example
        if (value == 15 || takeNextStep || 
            (firstStepCounter > 15 && repeat))
        {
            // code
        }
    }

    // Longer function declarations in two or more lines should place 
    // the opening brace in the next line to clearly mark the start of the scope.
    void someFunctionWithManyArguments(const std::vector<double> & vec1,
        const std::vector<double> & vec2,
        const std::vector<double> & vec3)
    {
        // code
    }


    // The following source code shows typical indentation rules
    void indentationAndOtherRules() {

        // recommendation: use 'if (' instead of 'if( ' 
        if (someCondition) {
            // code
        }
        // put else in a separate line and put code comments
        // like this before the else clause to document what's
        // done in the else block
        else {
           //
        }

        // put spaces between ; separated tokens in for loops
        for (i=0; i<20; ++i) {
        }

        // indent switch clauses like the example below
        switch (condition) {
             case WELL: 
                 // code
                 // more code
             break; // break on same level as case

             // document case clauses before the case
             case SICK:
                 // code
             return "sick";

             // when you declare local variables within switch
             // open a dedicated scope
             case DONT_KNOW: {
                 int var1;  // local variable, only valid for case clause
                 // code
             } 
             break; 

             // if you have many short case clauses, you can use properly indented one-line versions
             case ABIT_SICK        : return "a bit sick";
             case ALITTLEBIT_SICK  : return "a little bit sick";
             case QUITE_WELL       : break;

             default: ; // always implement the default clause, even if 
                        // not possible to avoid compiler warnings
        } // switch (condition)
        // in long nested scopes, document the end of the scope as done in the line above

        // another example of documented nested scopes
        for (k=0; k<10; ++k) {
            for (j=k; j<10; ++j) {

                // lots of code

            } // for (j=k; j<10; ++j)

        } // for (k=0; k<10; ++k)
    }

Common Rules

  • Indentation is done with tabs, editors set to 4 spaces per tab, set your IDE/Editor accordingly.
  • Doxygen comments are to be used, in the format {{{/*! */}}}
  • Doxygen Documentation should be placed exclusively in header files for all functions and variables, in implementation files only standard C/C++ comments should be used
  • Examples should be provided with typical operations (using code/endcode tags).
  • Header files get the .h extension, Implementation files get .cpp (or .c for plain c-code).
  • Source code files (headers and cpp files) are prefixed, for example IBK_, with the following naming conventions:
        ClassName
        IBK_ClassName.h
        IBK_ClassName.cpp
  • Include guards for header files are written exactly as the header file name, but with the .h substituted with an H

    IBK_ClassNameH
    
  • Class declaration access levels are to be ordered: public, protected, private

  • All source code is to be wrapped in the namespace IBK

Example:

    namespace IBK {
        // code
    } // namespace IBK
  • Member variables are prefixed with m_ to distinguish them from local variables/function arguments.

Example:

    // m_ style
    m_myMemberVariable;
  • Typenames should be camel-cased by default, this holds for: enums, structs, template typenames
  • global function use _ notation to distinguish them from member or local functions

Example:

    // names of global functions are composed of lower case words connected with underscores
    std::string format_time_difference(double delta_t);
    // descriptive names are to be used
    void break_string(const std::string& msg, std::vector<std::string>& lines, int line_width);
  • in the naming convention for classes we distinguish between template and normal classes:

Regular Classes and Structs

Regular classes are given Camel-cased-names. Apart from that, the rules for template classes apply as well. Consider the following code example as guide for valid naming conventions.

    /*! Every class should be documented, at least briefly.
        It is useful to include some code examples using doxygen source code
        snippets on how to use the function. This allows users to copy source code snippeds from
        the documentation. For example:
        \code
        // instatiate the class
        MyTestClass c;
        // set the property
        c.setFirstNumber(15);
        // check if the number is valid
        if (c.isValid()) {
            // code
        }
        \endcode
    */
    class MyTestClass {
    public:
        /*! Doxygen comments are used for documenting functions.    */
        MyTestClass();

        /*! A typical function declaration starts lower case and continues
            in camel-case 
            \param n    Function arguments are documented individually, 
                        unless they are trivial.
            \return Return values are documented when meaning is not
                    clear from function description.
        */
        bool readTestClassList(int n);
    
        /*! Getter functions for property-like variables get the name of the
            variable without the m_
            The declaration for the getter function should always
            be declared as 'const'.
        */
        double firstNumber() const;

        /*! The matching setter function is prefixed with 'set' followed by
            the capitalized property name. */
        void setFirstNumber(double val);
    
        /*! Getter-Functions for boolean state variables are prefixed with 'is'. */
        bool isValid() const;
        /*! Setter-Functions for boolean state variables are prefixed as usual with 'set'. */
        void setValid(bool valid);
    
        /*! Publicly accessible variables do not get any prefixes or suffixes. */
        double maxValue;
    
    private:
        /*! Private member variables that have getter and setter functions
            are named just as the getter function, but prefixed with a 'm_'.
        */
        double m_firstNumber;

        /*! Member variable corresponding to isValid() and setValid().
            \see isValid()
            \see setValid()
        */
        bool m_valid;
    };

Namespace Prefixes

The using namespace xxx statement must not be used in header files at all. In general, namespace prefixes should be included in the code to identify the origin of a symbol/function. The key motivation for this is to help readers of the code to find out which library a functio/class belongs too. Even if it looks a bit verbose, as in the following example, still include the namespace.

    std::cout << std::fixed << std::setprecision(4) << std::setwidth(10) << std::right << value << std::endl;