pmp-library  1.0
Coding Style

This section describes some of the coding standards that are being used in the pmp-library source tree. Although these standards should never be regarded as strict requirements they are still important to establish a minimum level of consistency through the code base as well as to facilitate code comprehension for developers and library users. Therefore, if you would like to contribute code to the pmp-library please make sure your code adheres to the conventions outlined below.

Naming

Types

The names of user-defined types such as classes, structs and enums use CamelCase notation. The names of persons such as Cholesky or Delaunay are properly capitalized as well.

class SurfaceMesh { ... };
struct Flags { ... };
enum TextureMode { ColdWarmTexture, CheckerboardTexture, OtherTexture };

Functions

Function names are written using snake_case. All characters are lowercase. Separate words by underscores.

class MeshViewer
{
bool load_mesh(const char* filename);
};

Variables

Variable names use snake_case notation. All characters are lowercase. Separate words with underscores. Class member variables have an underscore _ suffix.

int global_var;
class ExampleClass
{
protected:
double member_variable_;
static double static_member_;
};

Exception: Public members of a struct holding just a group of variables may omit the underscore suffix:

struct NearestNeighbor
{
Scalar dist;
SurfaceMesh::Face face;
Point nearest;
int tests;
};

Exception: For the sake of similarity with common mathematical notation, we sometimes use uppercase letters, e.g., to denote matrices when solving a linear system:

Eigen::SparseMatrix<dobule> A(n, n);
Eigen::MatrixXd B(n, 3);

File Names

File names follow the naming rules for user-defined types, i.e., they use CamelCase naming. Implementation files end with .cpp and header files end with .h.

Exception: Files for building an executable use an all lowercase name, e.g., mpview.cpp to build the mpview executable.

Formatting

Blocks

The expressions following an if, else, while, do ... while or for statement should always be enclosed in braces. The braces enclosing a block should be placed in the same column, on separate lines.

if (foo == bar)
{
std::cout << "baz" << std::endl;
}
else
{
std::cout << "barbaz" << std::endl;
}

Line Length

Lines should not exceed more than 80 characters. There should only be one statement per line.

Indentation

Use spaces instead of tabs. Indent the code by four spaces for each level of indentation. Avoid trailing whitespaces at the end of a line as well as on empty lines.

Miscellaneous

This section describes some basic programming conventions developers should adhere to.

Declaration order

Group the sections of a class in the following order: public, protected, private.

Use one section for each type of access specifier.

Omit empty sections.

Within a section use the following order: typedefs and enums, constants, constructors, destructor, operators, methods, static methods, data members, static data members.

Comments

Use C++-style comments, i.e., // my comment.

Doxygen Documentation Comments

We use Doxygen to generate our API documentation. All public types and interfaces should be properly documented. This usually includes a short abstract not longer than a sentence as well as a more detailed discussion of what the function does. We use //! for doxygen comments. The following is an example what a full documentation comment could look like:

//! \brief Does foo.
//!
//! \details Does foo and nothing else. If \p use_bar argument is true uses the
//! bar method instead of foo.
//!
//! \param[in] use_bar toggle to switch method
//! \param[out] results filled with results from foo
//!
//! \returns true on success.

Another good practice is to document pre- and post-conditions for performing an operation. Those can be documented using the \pre and \post Doxygen special commands.

Use Doxygen comments only in the header file. Do not repeat the same information in the implementation file. Instead, provide specific details on the implementation at hand, i.e., how was the functionality implemented, and why was it implemented in this manner?

Include Guards

Use the #pragma once compiler directive at the beginning of each header file in order to protect against multiple inclusion. Although this is not officially part of the language this feature is supported by all major compilers and is much more convenient than conventional header guards.

Namespace

Use the pmp namespace in order to avoid conflicts. In source files, do not add an additional level of indentation due to the namespace:

namespace pmp {
class ExampleClass
{
...
};
}

Boolean Prefixes

Use meaningful prefixes for bool variables or functions returning booleans, e.g., has_colors() for a function or is_done for a variable.

Naming Consistency

Consistently name dynamic properties, e.g., v:scalar for vertex scalars or f:normal for face normals. Similarly, consistently name iterators and circulators by their entity type (Vertex, Halfedge, ...)

Type Consistency

Try to avoid conversion issues by using consistent types, e.g., use std::size_t when storing values from STL functions such as the size() member function of a std::vector. Use the C++11 auto keyword to let the compiler determine the proper types.

Structs vs. Classes

Use plain structs for data objectes providing nothing but a collection of other data types, e.g., a collection of parameters passed to a functions. Such a struct should not contain any further functionality than what is required for construction, destruction, or initialization. In contrast to class member variables, struct members do not have a underscore _ suffix.

Scoping

Localize variable scope and avoid declaring all variables at the beginning of a function or code block.

Using clang-format

Please use the clang-format tool and the corresponding .clang-format configuration file from the repository to properly format your code. We also provide a convenience CMake target to run clang-format on all source files:

$ make clang-format

This requires that the clang-format executable is found during CMake configuration. The exact path to the executable can be specified using

$ cmake -DCLANG_FORMAT_EXE=<path/to/executable> ..

Look for a line like this

-- clang-format found: /usr/bin/clang-format

in the CMake configuration output.

In case you want to preserve the special formatting of a particular code block such as a matrix intialization add the // clang-format off and // clang-format on directives around this block:

// clang-format off
Mat4<Scalar> m;
m(0, 0) = x[0]; m(0, 1) = x[1]; m(0, 2) = x[2]; m(0, 3) = -dot(x, eye);
m(1, 0) = y[0]; m(1, 1) = y[1]; m(1, 2) = y[2]; m(1, 3) = -dot(y, eye);
m(2, 0) = z[0]; m(2, 1) = z[1]; m(2, 2) = z[2]; m(2, 3) = -dot(z, eye);
m(3, 0) = 0.0; m(3, 1) = 0.0; m(3, 2) = 0.0; m(3, 3) = 1.0;
// clang-format on

See Also

The above coding standard is necessarily incomplete. Therefore, we list some additional references and pointers to useful resources regarding C++ coding standards and best practices: