Wiki

Clone wiki

lifev-release / dev-guidelines

Go back


Developer guidelines

This short guide provides some useful rules to improve the development of LifeV Library. Following the example of Trilinos and other well structured libraries, we have understood that it is mandatory to follow some common rules in the implementation of our code. A better organization of classes and files, together with the development of a complete and exaustive documentation, will help old and new developers to take advantage of all the available features of our library, improving productivity and results.


Nomenclature of classes and methods

This is a short list of rules for the nomenclature of files, classes and methods.

  1. All declarations should be placed into *.hpp files. Method definitions should be inside the *.cpp files. In case of template classes and/or methods, the definitions should be placed at the end of the *.hpp files. Inlined methods must be in the *.hpp files: short inline methods (usually no more than one line), should be defined inside the class definition (without the inline keyword), while longer inline methods should be defined at the end of the *.hpp files, using the inline keyword.

  2. LifeV currently uses type definitions at the beginning of the class. It was intended to introduce more generality in the code. However, this construct has to be used with caution, since it can lead to types pollution, duplication of types and possible (runtime) bugs.

    The syntax for types and pointer types currently used is the following:

    typedef Epetra_FEVector                       vector_Type;
    typedef const Epetra_FEVector                 vectorConst_Type;
    
    typedef std::shared_ptr< vector_Type >        vectorPtr_Type;
    typedef std::shared_ptr< vector_constType >   vectorConstPtr_Type;
    
  3. Classes and structures should start with the capital letter. In case of composed names, each word should start with capital letter.

    class datatime; //NO!!!
    
    class DataTime; //OK
    
  4. File names should start with the capital letter also. Moreover, the name of the file should be equal to the name of the class/structure coded in it.

  5. Packages of classes/files which are related to the same project and application should start with a common prefix (as in Trilinos)

    // FSI Classes as they are now:
    FSISolver
    FSIOperator
    exactJacobian
    fixedPoint
    DataFSI
    
    // FSI classes as they should be:
    FSISolver
    FSIOperator
    FSIOperatorEJ ( here the common prefix FSIOperator make it clear that
                    FSIOperatorEJ is strictly related to FSIOperator )
    FSIOperatorFP
    FSIData
    
  6. In case of composed names for methods, each word (starting from the second one) should start with the capital letter.

    void matrixvectormultiplication( matrixtype& matrix ); //NO!!!
    
    void matrixVectorMultiplication( matrix_Type& matrix ); //OK
    
  7. All variables and methods names should clearly describe what they are doing. In addition, do not use the underscore at the beginning of input/output variables.

    // This is a true bad example taken from EpetraMatrix class
    
    // What do these mean?
    void set_mat_inc( UInt row, UInt col, DataType loc_val );
    void set_mat( UInt row, UInt col, DataType loc_val );
    
    // Now it is clear!
    void insertSingleElement( UInt row, UInt column, data_Type value );
    void replaceSingleElement( UInt row, UInt column, data_Type value );
    
  8. Do not use abbreviations in the code! They prevent its readability.

    void elByElMul( vtype& v1, vtype& v2 ); //NO!!!
    
    void elementByElementMultiplication( vector_Type& vector1,
                                         vector_Type& vector2, ); //OK
    
  9. Member variables in LifeV should start with M_. Moreover all the rules for the names of methods are also valid for class members.

    private:
    
        Real        vel;   //NO!!!
        std::string File;  //NO!!!
    
        Real        M_velocity; //OK
        std::string M_fileName; //OK
    
  10. Static members variables in LifeV should start with S_.

    private:
    
        static Int counter;   //NO!!!
        static Int M_counter; //NO!!!
    
        static Int S_counter; //OK
    
  11. Avoid function declarations without argument name.

    void setDataFile( const std::string& ); //NO!!!
    
    void setDataFile( const std::string& fileName ); //OK
    
  12. Any method that does not change the state of the object must be made const (in particular getters). In addition all get methods should return a const object by reference (even if it is a std::shared_ptr). For clarity, the const directive should appear before the type of the object.

    matrix_Type&    matrix();     //NO!!!
    matrix_PtrType& matrix_ptr(); //NO!!!
    
    const matrix_Type&    matrix() const;     //OK
    const matrixPtr_Type& matrixPtr() const; //OK
    

    NOTE: if a method is returning a reference, and you want to be able to use the returned reference as lvalue (i.e. to modify the content) you must provide both the const and non-const version, even if it requires some code duplication:

    const matrix_Type& matrix() const;
          matrix_Type& matrix();
    

    Providing only the non-cont version will indeed impede to use the method on const objects.

  13. Arguments should preferably be passed by reference and (compulsorily) by constant reference if they are not modified by the function/method (i.e. are used just an input). In addition, all set methods should start with the prefix set.

    void matrix( matrix_Type matrix );           //NO!!!
    void matrix_ptr( matrixPtr_Type& matrixPtr); //NO!!!
    
    void setMatrix( const matrix_Type& matrix );         //OK
    void setMatrixPtr( const matrixPtr_Type& matrixPtr); //OK
    
  14. Use white space to improve the readability of the code.

    M_result=(a+b)/(c*d)             //NO!!!
    
    M_result = ( a + b ) / ( c * d ) //OK
    
  15. All principal classes should have a method showMe(),

    void showMe( std::ostream& output = std::cout ) const;
    

    which outputs on output (which may be the standard output or a file) the state of the class (i.e. the content of the global and local variables).


Conventions

This is a short list of programming and general conventions that should be used working with LifeV library.

Programming conventions

  1. Each file should include another one only if it is strictly required; as a general rule, try to shorten as much as possible the list of #include directives.

  2. Use the typedefs aliases Real, Int and UInt, instead of the built-in types double, int and unsigned int. This helps making code changes afterwards. All the type definitions are contained into LifeV.hpp.

  3. Use the right pointer type when necessary (raw pointer, std::shared_ptr, or std::unique_ptr). Do not use boost pointers, but the std ones.

  4. All the members should be private or protected (if necessary).

  5. Don’t use reference types for members inside classes. Of course use them for functions and methods arguments.

  6. using directives should be avoided entirely, especially in header files. They cause wanton namespace pollution by bringing in potentially huge numbers of names, many (usually the vast majority) of which are unnecessary. The presence of the unnecessary names greatly increases the possibility of unintended name conflicts not just in the header, but in every module that includes the header. Moreover using namespace declarations should never appear in header files. The meaning of the using declaration may change depending on what other headers have included before it in any given module.

  7. Use const keyword when possible. This helps the compiler and the developers reading the code. Moreover, it enhances debugging. See http://en.wikipedia.org/wiki/Const-correctness for more details on the use of const.

  8. Use the C++ cast utility (static_cast) instead of implicit compiling casts. This will avoid warnings and will keep each cast visible for future debugging. See http://www.cplusplus.com/doc/tutorial/typecasting for more details on the use of cast commands.

  9. the classes should always have an empty constructor and a virtual empty destructor, being filled by specific calls to setters or methods. The copy constructor, if not required / implemented, should exist as a private method without implementation. Declare explicit all constructors which may take only one argument, unless there is the very need of having implicit conversion (it may be required in certain constructs). In addition, avoid to assign member in the body of the constructors if you can just initialize them (of course in the intialization list).

  10. For debugging purposes please use the debug.hpp class features:

    #ifdef HAVE_LIFEV_DEBUG
        debugStream( 3000 ) << "MyClass::myFunction  myVariable = "
                      << M_myVariable << "\n";
    #endif
    

    Thanks to this syntax no output will be displayed, unless the debug mode is enabled and an enviroment variable is set in the shell with the command export DEBUG=3000. This will avoid a lot of unuseful outputs especially on parallel runs. For the complete list of debug numbers (that you can enrich) see the file debug.areas in the library. In addition, use the __LINE__ and __FILE__ C preprocessor macros in debugging messages.

  11. For assertion purposes please use the lifeassert.hpp class features.

General conventions

  1. To uniform the code inside the library, all developers have to use the same notation and in particular the same indentation style. The style used in LifeV is the BSD/Allman Indent style. Please see http://en.wikipedia.org/wiki/Indent_style#Allman_style_.28bsd_in_Emacs.29 for more details about this style and its inventor. Moreover please note that both Eclipse and Emacs (and probably also other editors) have a special utility to help follow this style.

    cf also A-Style.

  2. Before committing any code, be sure that all tabs have been converted into 4 spaces, and that your editor has removed endline spaces.

  3. Before committing any modification in the library, always check that these modifications have not broken any test present in the testsuite.

  4. Do not introduce warnings in the library: C++ compilers help us for free, giving important hints about what is wrong or dangerous in our code. Why should we ignore it?!!

  5. Always include a complete description of all the modifications in the commit message, to allow other developers to easily understand what is changing in the library.

  6. About the debugging procedure: if you find a bug in the code, fix it and then use the lifev-dev mailing list to notify all the developers about the bug. A simple commit with short description is not sufficient!

  7. All the documentation, files and variables names, comments and more generally any kind of text must be in english language.

  8. Don’t write comment or documentation using all caps look letters. Use them only for titles, or specific words.

  9. In order to improve readability of the code, headers should contain sections (see Section doxygen for doxygen syntax) grouping all the similar methods. Basically all the classes should contain at least these sections:

    Public Types

    : containing the public enum and type definition(s).

    Constructors & Destructor

    : containing the constructor(s), the copy constructor and the destructor(s).

    Operators

    : containing the operators defined in the class, such as the operator= for making copies.

    Methods

    : containing all the general methods of the class. Note that a method performs operations on private variables and it is not just a setter or getter function.

    Set Methods

    : containing all the set methods (starting with the prefix set)

    Get Methods

    : containing all the get methods.

    Protected Methods

    : containing all the protected methods.

    Private Methods

    : containing all the private methods.

    Note that protected and private methods are also grupped and appears after all the other methods. Finally regarding the members of the classes, they should be placed after (and outside) the corresponding group of methods (i.e. protected members, after all the protected methods & private members, after all the private methods).


Testsuite development

All new packages and main classes should have a working test in the testsuite: this is mandatory to allow easy maintenance of all the classes in the library. Moreover, it is important to have a working test for the night compilation of the library, that runs all the tests in order to verify their status.


Doxygen documentation

All classes should have documentation lines explaining in a concise but thorough way their usage. Doxygen provides an easy and general format to obtain this result.

How to obtain LifeV doxygen documentation

If the doxygen and graphviz packages are present on your machine, you can generate the LifeV doxygen documentation by typing make apidox at the end of the compilation process. The documentation is accessible at the following path:

$LIFEV-COMPILATION-FOLDER/doc/api/html/index.html

How to add doxygen style in C++ classes

In order to be fully documented, the code should follow some simple rules which are described on the doxygen website www.doxygen.org. In particular the main doxygen style commands are described at the following page http://www.stack.nl/~dimitri/doxygen/manual.html.

To make things easier for all LifeV developers, a general LifeV example class has been generated and attached to this guide as an appendix (see ExampleClass.hpp and ExampleClass.cpp). It contains the general layout structure for any new class. All the future classes in LifeV should be developed starting from these two files, which contain everything to make doxygen work properly.

In particular, the header file is divided into 4 sections:

  1. A general header containing license and copyright information that should appear at the beginning of each file.

  2. A short description of the file content, with at least a brief description of the file content (using command @brief), the author(s) list (using @author) and the date (using @date). In particular, for the description of the file it could simply be the list of classes contained in the file, which are fully described later.

  3. A full description of the class, describing its purpose and its external interface. It should be placed before its declaration. The first line (starting with //!) contains the name of the class and a very short description of it (one line maximum). In the following block of lines (starting with /*! and finishing with /*) the full description of the class should be given. This is a good place also to describe the list of parameters that are required by the class and that should be provided from a data file.

  4. A detailed description of all the public methods contained in the class, and their input and output parameters. All the methods are grouped using the syntax:

    //! @name Name of the group (for example Methods)
    //@{
    
    //@}
    

    and inside each group all the methods contain a full description of their usage such as:

    //! Short description of the equivalence operator
    /*!
       Add more details about this operator.
       NOTE: short description is automatically added before this part.
       @param T  TemplateClass
       @return   Reference to a new TemplateClass with the same
                 content of TemplateClass T
     */
    TemplateClass& operator=( const TemplateClass& T );
    

    where all the parameters are described using the syntax:

    @param inputParameter   Description of the input parameter
    @return                 Description of the output parameter
    

    Note that for input parameters (command @param) the first word is the name of the parameter, followed by his description, while for output parameters (command @return) there is only the description.

If there are more classes (or structures) in the file, point 3 and 4 should be repeated for all of them.

For the .cpp file the first two sections are exactly the same of the *.hpp file. Then all the implementations of the methods should be placed in the same order as in the header file. To keep the same group division given in the header, the following comment could be used to identify the beginning of a group:

// ===================================================
// Name of the group
// ===================================================

Authors: A. Cristiano I. Malossi, Simone Deparis. Updated by: Andrea Bartezzaghi.

Updated