next up previous contents
Next: 3 Recommendations for Usage Up: c++_coding_guidelines Previous: 1 Introduction   Contents

Subsections

2 Guidelines for Names and Structuring

2.1 Names

Today all C++ compilers support namespaces, and so do we. We use theplu as our major namespace, and use nested project namspaces within the theplu namespace. In section 3.1 we explain our view on namespaces usage.

All names should be descriptive unless they are better left as a single letter or word. Words in names should be separated by underscores, except for class names and enum keywords. In class names and enum keywords the first letter in each word should be capitalized and underscores should be avoided. Private member variables should end with an underscore. Functions, variables and enum tags should preferably be all in lower-case. Global variables should be prefixed with upper-case GLOBAL_ and static variables (but not static member variables) with upper-case STATIC_.

class MyClass
{
public:
  enum State { active, inactive };
  double calculate_number(void) const;

private:
  double value_;
};

double MyClass::calculate_number(void) const
{
  extern int GLOBAL_factor;
  return value_*GLOBAL_factor;
}


2.2 Header File Structure

The complete body of header files should be protected against multiple-inclusion, as follows:
#ifndef symbol
#define symbol
<the complete body of the header file>
#endif
Note, no lines of code or comments should be outside the #ifndef ... #endif construct. This allows compilers to optimize the reading of header files.

The symbol used in the conditional compilation statement should be made up from the project name and module name. If the module name is MyClass.h and the module is in project myproject then the symbol name should be _theplu_myproject_myclass_. The idea of this symbol is that it should be unique in the environment where it is used.

The multiple-inclusion protection should be followed by #include statements that describe which interfaces the module depends on. Typically a module depends only on the interfaces of base classes, classes that are contained by value or classes whose member functions are called within inline functions.

Next comes the forward declarations of the classes that the module uses and contains. Forward declarations should always be preferred to #include statements, which only should be used when necessary. This will minimize code dependencies, and make the programmer more conscious about what is used and what is not used in their code.

The #include statements and forward declarations should both be structured in blocks (separated by empty lines). Each block should contain modules of similar origin and each block should be sorted alphabetically. If needed, a block should begin with a comment explaining it.

All header files must include config.h first (if it's needed), followed by the primary header. So for example, File.cc should include its primary header File.h first (config.h omitted here), before other header files. This guarantees that the completeness of each header file is tested during compilation. After the primary header follows inclusion of header files located in the same directory followed by header files belonging to the same project but located in other directories. Then 3rd party project header files are included and last standard header files are included.

#include "config.h"

#include "File.h"

// Classes from this project
#include "OtherClass.h"

#include "path/to/other/dir/in/project/SomeClass.h"

// Classes from project X
#include <MyOtherProjectClass.h>

#include <iostream>
#include <vector>

Next comes the class specifications, followed by additional operators such as operator$<<$ and finally comes the implementation of inline member functions if these are not defined already in the class declaration. We do not support the idea of collecting all inline functions in a separate file since this will only give another file to keep track of.

2.2.1 Class Declaration Structure

All public declarations come first, then all protected declarations followed by all friend declarations and finally all private declarations. The indentation and spacing should be consistent and readable. Generally, we prefer not to indent public:, protected: and private:, while we indent all the declarations.
class MyClass
{
public:
  double public_function(void);

protected:
  int protected_function(void);

private:
  string name_;
};
The member functions should in each section start off with the constructors, followed by the destructors, then follows the member functions in alphabetical order, and finally the operators are declared in the order defined in Appendix B.

2.2.2 One Class Definition per Module

We generally keep the rule of having one class definition per module. Each class definition is in its own header file. Sometimes, if classes are tightly coupled, we make an exception.

2.2.3 Comments in Header Files

We generate our documentation from our C++ files using Doxygen (see [2]). This reduces the visibility of the structure. We think this is outweighed by not having to duplicate the protoypes of functions in a separate documentation. Before each declaration or definition we want to document we place the following1:
/**
   Some text documenting the class MyClass
*/
class MyClass

Try to keep comment line lengths within the terminal character limit, i.e less than 80 characters per line. This will make the comments more readable.

2.3 Implementation file structure

We generally keep the rule of placing all the non-inline definitions of the member functions for a class in one module, and that module will not contain definitions of member functions for any other class. Sometimes, if we have made an exception in a header file, we make the same exception in the corresponding implementation file and include the definitions of member functions from several classes in one module.

We begin the implementation file with the necessary #include statements and we structure them in the same way as in the header files (see Section 2.2) with the exception that the class's own header file must be included first.

Next comes the definition of member functions and they are ordered in the same way as a section in the class declaration. At the end we have the definitions of additional operators such as operator$<<$.

2.3.1 Comments in the Implementation Module

Most member functions are fully explained by the names and the comments in the header file. If tricky algorithms, that must be explained, are used in a function then we preface its definition with a comment describing the algorithm. Within the code we try to limit our comments to one line. Doxygen supports documentation in several places, but we prefer to only put comments extracted by Doxygen in the header files (cf. [2]).

2.4 Two Issues About Style

Two issues that always come up when programmers with different backgrounds get together are the placement of braces and indentation. In this section we give you our position on these two eternal questions.

2.4.1 Brace placement

We prefer to put the begin brace on the same line as the controlling statement, while we keep the end brace on its own line and at the same indent level as the controlling statement. There are two exceptions to this rule as outlined below.

In general we use the

if (mycondition) {
  // statements inside the if
}
everywhere where braces are used. The exceptions are brace usage in function definitions
MyClass::public_function(void)
{
  // function body
}
and in inline declarations we prefer a more compact style
class MyClass
{
public:
  inline double return_zero(void) { return 0; }
  inline double another_public_function_with_a_long_name(void)
  { return some_function_returning_double(); }
}
where the second style is used when the line has to be split for readability. If declaration and definition for inline functions are separated then the function definition style is preferred.

2.4.2 Indentation

The question of indentation is really not an issue, every indentation level should strictly be a tab character. The problem is to get your favourite text editor to show and treat indentation properly (see Appendix A for information on how to configure a few common editors).

2.5 Summary

So far we have been concerned with guidelines that facilitate the communications among our developers. The important thing with these guidelines is not the guidelines themselves but to stick to a chosen set of guidelines.


next up previous contents
Next: 3 Recommendations for Usage Up: c++_coding_guidelines Previous: 1 Introduction   Contents
Jari Häkkinen 2016-06-13