Skip to content

Latest commit

 

History

History
662 lines (596 loc) · 18.4 KB

cpp.md

File metadata and controls

662 lines (596 loc) · 18.4 KB

General C++ Style Guide

Namings

  • oc-token = open close token such as parenthesis [,], (,), {,} and <,>
  • prefix tag = all characters before an oc-token
  • operator = math operator symbols like +, -, / ...
  • regex = regular expression to indicate where spaces must be placed
    • {...} = optional definition of ...
    • [:...:] = explicit placement of ...

1. Files and Encoding

  • files must be encoded in ASCII
  • LF ( line feed, \n , 0x0A ) must be used for new lines
  • each file must contain
    • copyright notice
      • the corresponding license header, author(s) + current year ( if changed - and a commit of a file means a change )
      • you never remove an author or a year, you only add to them
    • a #pragma once as guard to prevent double includes ( do not use #ifdef guards ) in header files
    • doxygen @file ... documentation directly after the license header and before the #pragma once
  • includes:
    • order from the most specific/specialized to the most general one
    • objects used from an include can be added in a comment but should at most be used for stable third party includes (can be used for includes from C++ STL, Boost, ...)
    • two empty newlines between the last include and the start of the code, one around #pragma once
/* Copyright 2017 Max Mustermann
 *
 * <YourLicense header>
 */

/** @file file.hpp
 *
 * This is optional but can be useful for important files that do not only
 * implement a single class which can get the full docstring for it.
 * keep this text in one block, otherwise Doxygen will split it with wrong
 * association with the next upcoming code or namespace.
 */

#pragma once

#include "myproject/file.def"

#include "myproject/myTypes.hpp"
#include "myproject/unStableApiTraits.hpp" // is_const<>, is_trait<> -> this should be avoided

#include <boost/mpl/vector.hpp>
#include <boost/type_traits.hpp> // is_void<>, is_union<>

#include <memory> // shared_ptr


namespace myProject
{
    /* ...
     */
} // namespacee myProject

2. Project

  • each project must have a global namespace which covers all the code but not includes
  • the namespace structure
    • is equal to the folder structure
    • a file can have additional namespaces which are not represented by folders
  • the include folder contains as a first folder the project name e.g., include/picongpu, include/haseongpu
  • folder names must be written in camel case and begin with a lower case letter
  • the project name must begin with but can be fully composed of lower case letters (for folders and namespaces); since this is a simplifcation for coding, the official spelling might differ

3. Global Rules

  • wrap a line after column 80
  • no TABS, use four (4) spaces per indentation level
  • no code alignment (only indentation)
  • names are always written in camel case syntax e.g., ThisIsCamelCase, thisIsAlsoCamelCase ( except: macros and defines )
  • if there is no content between { }, < >, [ ] or ( )
    • the oc-token token must be on the same line
    • one space between both oc-tokens ( regex: <[:space:]> )
  • oc-token { }
    • { is always placed on a new line ( same indentation level as the line before ) and followed by a new line
    • } is always placed on the same indentation level as the opening token
  • oc-token ( )
    • for function / method signatures
      • ( is placed after a prefix tag without a space before e.g., method( );
      • ( must be followed by a new line ( except: if ( ) empty )
      • ) is always placed on the same indentation level as the opening token ( except: if ( ) is empty )
    • for expressions e.g., ( a + 1 ) * 5;
      • can be placed on one line
      • after 80 characters a new line must follow
      • after first new line start indentation, see section Operators
    • for function calls e.g., foo( this );
      • for one function parameter use one line
      • for more than one function parameter place each indented on a new line, see section [Function Calls](cpp.md#9-Function Calls)
  • usage of end-of-line spaces is not allowed
  • it is not allowed to place a space before ;
  • semicolon ; is always followed by a new line, except in short (one-line) for statements

4. Comments

  • multiline comments
    • begin with /* ( or /** for doxygen) followed by a space ( regex: /*[:space:] )
    • end with */ on a new line
    • each line between begin and end starts with * ( regex: [:indentation:][:space:]*{[:space:]comment} )
  • single line comments begin with // (or //! for doxygen) followed by a space ( regex: //[::space:] )
  • it is not allowed to format comments with long symbol repetition e.g., // ################################
/*#################################################
 *#   @brief block formating this is not allowed  #
 *#                                               #
 *#   text text                                   #
 *#################################################
 */
  • doxygen inside comments is highly encouraged, see section doxygen
/* this is a multiline comment
 * where the end tag is on a new line
 */

// this is a single line comment

5. Doxygen Comments

  • oneline comments via //! should only be used when a brief title alone is descriptive enough (e.g. definition of a member variable)
  • for all other, multiline comments must be used for doxygen comments
  • brief ("title")
    • begins with /**
    • should be a single line comment
  • long description
    • is separated with an empty line
    • is not aligned with the brief line
  • use @ for doxygen commands e.g., @param, @return, @{
  • commands which should only rarely be used e.g., for code parts that are included from (license compatible!) third party projects (use the copyright header for full files or files that mainly consist of the external code)
    • @author
    • @date
    • @copyright
  • use @f[ / @f] or other formula commands to describe equations in latex
/** this is allowed
 *
 * text text
 */
/** brief description
 *
 * long description is not aligned with brief line
 * @warning not thread save
 * @todo pass control arguments as enum
 *
 * @tparam T_Foo is our first template parameter
 * @param a is our first parameter
 * @param b is our second parameter
 * @return sum of input parameters
 */
template<
    class T_Foo
>
HDINLINE static
int
functionName(
    T_Foo a,
    double b
)
{
    return 0;
}

//! add fairy dust
bool const addMagic = true;

6. Namespaces

  • namespaces begin with a lower case letter
  • it is not allowed to indent nested namespaces
  • each namespace is defined on a separate line
  • the closing parenthesis } of a namespace must be placed on a new line together with a namespace name as comment ( regex: }[:space:]//[:space:]namespace[:space:]namespaceName$)
  • code inside the deepest namespace is indented
namespace clair
{
namespace bob
{
namespace alice
{
    // this one gets indented
    struct FooMe;

} // namespace alice
} // namespace bob
} // namespace clair

7. Preprocessor

  • macros are written ALL_UPPERCASE and can use _
  • # is not indented
  • # is interpreted as one of the four (4) spaces for indentation
  • indent nested preprocessor commands
  • macros with newlines: align \ symbol to column 80
#if( VARIABLE_BOB == 1 )
#   if( VARIABLE_ALICE == 1 )
#       define HPC +1
#   endif
#endif
  • #ifdef should not be used to check own definitions ( use #if( MYDEFINEDPARAM == 1 ) instead )
  • avoid preprocessor macros and use C++11 features if possible
  • comment preprocessor macros
#define MY_MACRO( )                                                           \
    template<                                                                 \
        /* typename T0, ..., typename TN */                                   \
        BOOST_PP_ENUM_PARAMS( N, typename T )                                 \
    >                                                                         \
    HDINLINE                                                                  \
    void                                                                      \
    /*         ( T0 const, ... , TN const           ) */                      \
    operator( )( BOOST_PP_ENUM_PARAMS( N, T const ) ) const

8. Operators

  • unary operators are placed directly before or behind the corresponding object e.g., i++, ++i and !enabled
  • binary operators
    • are surrounded by spaces e.g., x = a + b;
    • comma , ( d: in function signatures ) is always followed by a new line with same indent
  • operators except the comma operator do not require to be followed by a newline
  • after 80 characters a new line must follow
    • use the binary operator as the last code of the previous line
    • the first new line starts a new indentation block
void
method(
    int & bob,
    int const & alice = 5
)
{
    int bob( 10 );
    int alice( 10 * bob );
    bob = bob * alice + 12 * bob * bob * bob * bob++ * --alice +
        bob -
        alice;
    alice = charge * charge / (
        6.0 * PI * EPS0 * c2 * c2 * SPEED_OF_LIGHT * mass * mass
    );
}
  • alternative tokens for operators && ( and ), | ( bitor ), ... are not allowed

9. Function Calls

  • for one function parameter
    • use one line e.g., method( 2 );
    • parameter is surrounded by spaces
  • for more than one function parameter place each indented on a new line
  • ( ... ) are part of the caller (see above), no space to that caller
void
foo( )
{
    method( globalBob );
    method(
        globalBob,
        10
    );
}

10. Variable Declarations

  • the type qualifier const is placed right hand of the type that shall be const (East Const)
    • note: constexpr is not a type qualifier, but an expression qualifier, write it left of the type!
  • & (reference), && (universal reference), and * (pointer) must be surrounded by one space
    • note: multi-level pointers are packed though
int const * foo;  // pointer to const int value
int const * const foo; // const pointer to const int value
int * const foo; // const pointer to int value
int * foo; // pointer to int value
int ** foo;  // pointer to a pointer to int value
int * const * foo; // pointer to a const pointer to int value
int* foo; // NOT ALLOWED by the coding guide lines (missing spaces around `*`)
for( auto && e : vector ) // universal references are packed
{
}

constexpr int i = 5; // this is a constant expression with ints
  • if possible use direct initialization over the assignment syntax
int foo( 1 ); // should be preferred, if possible
int foo = 1; // should be avoided
  • use initialization lists in C++11
int foo{ 1 };
std::complex< double > bar{
    1.0,
    2.0
};

11. Function and Method Definitions

  • functions and methods are named in camel case with a beginning lower case letter
  • the function/method specifier must be placed on a separate line e.g., inline, static , __device__
  • the return type
    • must be placed on a separate line (sometimes the result is very long)
    • in C++11 auto specifier and trailing return type are used
    • trailing return type declaration is on a new line
auto
size( ) const
-> std::size_t;
  • function / method name must placed on a new line followed by the oc-token (
  • oc-token ( must be followed by a new line
  • oc-token ) is on a new line with the same indentation level as (
  • method type qualifiers are placed after )
  • code between { and } is indented
  • the function / method body { } is followed by an empty line
__device__ static
auto
square(
    int const a
)
-> int
{
    return a * a;
} // followed by an empty line

int globalA;

12. If Statements

  • omit curly { } braces for one-liners
  • indent the body
if( a == b )
   myFunction( c );

somethingElse( );
  • braces on new lines {
if( a == b )
{
     // do something
}
  • else is placed together with if on a line
if( a == b )
{
    // do something
}
else if( b == 1 )
    function( );
else
{
    foo( );
    bar( );
}

13. Switch Statements

  • case is indented
  • new line after :
switch( expression )
{
    case 'A':
        bob++;
        break;
    case 'a':
    {
        alice++;
        bob++;
    }
       break;
    default:
        moreBobs++;
}

14. Loops

  • complex parameter can be placed on separate lines
for( int i = 0; i < 10; ++i )
{
    // this is a simple loop
}

for(
    std::vector<bool>::iterator it = v.begin();
    it != v.end();
    ++it
)
{
    // this is a loop with long type names in its parameters
}
  • while loop
while( i != endOfLoop )
{
    // add your code here
}

while(
    i != endOfLoop &&
    d == 5
)
{
    // add your code here
}
  • do while loop
do
{
    // add your code here
}
while( i != endOfLoop );

15. Classes, Structs and Type Definitions

  • use camel case names that start with an upper case letter e.g., ClassBob, TypeNameBob
  • semicolon ; must be placed after the closing parenthesis }
  • code between { and };
    • access specifiers public, private and protected are not indented
    • all other code is indented
  • fixed prefix for each class is not allowed e.g., QApplication, QWidget (use namespaces)
  • inheritance
    • final specifier is placed inline with the class/struct name and surrounded by spaces
    • : is placed after the class name (regex: ClassName[:space:]:)
    • parent classes are indented and placed on a new line
    • each parent class is placed on a separate line
    • access specifiers public, private and protected and 'virtual' specifier must be placed inline with the parent class
  • in C++11 using shall be preferred over typedef
struct BobClass final :
    public BaseClass1,
    private BaseClase2
{
    BobClass( )
    { }
    
private:
    // C++11
    using OneOfMyBaseClasses = BaseClass1;
    // C++98
    typedef BaseClass2 TheOtherBaseClass;
};
  • constructor
    • : is placed after the constructor name and followed by a new line
    • member initialization is indented
    • each member is placed on a separate line
struct BobClass
{
    BobClass( ) :
        a( 1 ),
        b( 2 )
    { }
    
    int a;
    int b;
};

16. Template Declarations and Specializations

  • type ( class, struct ) template parameters shall be prefixed with T_ followed by an upper case letter camel case
template<
    typename T_Foo,
    typename T_FooBar
>
class Bob;
  • for type template parameters use typename over class
template<
    typename T_Foo,
    class T_FooBar // wrong keyword, should be `typename`
>
class Bob;
  • for template template parameters keyword class is allowed
template<
    typename T_Foo,
    template<
        typename, 
        int
    > class T_FooBar // keyword `class` is allowed
>
class Foo;
  • non-type template parameters shall be prefixed with T_ followed also by camel case starting with a lower case letter
  • type template parameter T_Type can be used without type renaming with using or typedef
template<
    std::size_t T_fooSize,
    bool T_fooBool
>
class Bob;
  • alias templates in C++11 with using keyword
// C++11
template<
    std::size_t T_fooSize
>
using BobWithoutBool = Bob<
    T_fooSize,
    true
>;
  • each template parameter is on a separate line
  • the oc-token > is on the same indentation level as the opening line
template<
    typename T_Bar,
    typename T_Foo
>
auto
foo(
    T_Bar const & extent,
    T_Buf & buf
)
-> mem::view::foo::detail::Bar<
    T_Bar,
    T_Foo
>; // this is not aligned with mem

// function template specialization
template< >
auto
foo<
    int,
    float
>(
    int const & extent,
    float & buf
)
-> mem::view::foo::detail::Bar<
    int,
    float
>
{
    return mem::view::foo::detail::Bar<
        int,
        float
    >( );
}

17. Template Instantiations

  • for one template argument
    • use one line e.g., method< 2 >( );, Foo< int >( )
    • parameter is surrounded by spaces ( regex: name<[:space:]param[:space:]>( ) )
  • for more than one template argument place each one indented on a new line
  • < ... > are part of the function, no space to the function name
void
foo( )
{
    method< int >( globalBob );
    method<
        int,
        float
    >(
        globalBob,
        10
    );
    
    FooClass< int >( );
    FooClass<
        int,
        float
    >( );
    
    // C++11
    using type = FooClass< int >;
    using type2 = FooClass<
        int,
        float
    >;
        
    // C++98
    typedef FooClass< int > type3;
}

18. Naming for Embedded Types

  • for result of a type trait ::type must be used
  • to get the type of an embedded value ::value_type
  • to access embedded values in types ::value
template<
    typename T_EmbeddedType,
    T_EmbeddedType T_embeddedValue
>
struct IntegralConstant
{
    static constexpr T_EmbeddedType value = T_embeddedValue;
    using value_type = T_EmbeddedType;
};

namespace traits
{
    template<
        typename T_Type
    >
    struct ValueType
    {
        using type = typename T_Type::value_type;
    };
} // namespace traits

19. Short Access to Member type/value in C++11

template<
    typename T_Type
>
using ValueType_t = typename traits::ValueType< T_Type >::type;
template<
    typename T_Type
>
using IsValid_v = traits::IsValid< T_Type >::value;

20. Special CUDA Syntax

  • you must not add spaces between the individual brackets of the CUDA kernel brackets <<< and >>>
kernel<<< 1, 1 >>>( ); // OK
kernel<< < 1, 1 > >>( ); // WRONG