Coding style

Coding style and best practices to preserve maintainability and consistent style across whole project.

Please note that if you have a good excuse to either break the rules or modify them, feel free to do it (and update this guide accordingly, if appropriate). Nothing is worse than rule that hurts productivity instead of improving it.

You can also take inspiration from other thoroughly written coding style guidelines of large projects:

In general, the main aim of this style is:

  • vertical and horizontal compression, fitting more code on a screen while still making it possible to have more files open side by side by wrapping on the 80th column
  • reducing ambiguity and mental overhead with clearly defined indentation and spacing rules
  • encouraging C++11 and new-style CMake workflow instead of C++03 and pre-3.0 CMake style

Text files in general

Each file must have one blank line at the end (Git will warn you in the diff if it's not the case), indentation is done exclusively with spaces (4 spaces). Trailing whitespaces are not permitted (and you should set up your Git installation to warn about that). Files in the repository should be with LF line ending by default. If CR+LF line endings are required for particular files, they have to be explicitly listed in .gitattributes file.

Logic sections of the code and documentation paragraphs are always separated with not more than one empty line to save vertical space. Sentences are always separated with only one space.

The text (and code) should be wrapped around 80th column to make it possible to view more files alongside each other without breaking their layout, long single-line statements are allowed unless it hurts readability.

CMake code

All CMake functions and macros (e.g. add_executable(), set()) are lowercase, keywords (e.g. FILES, DESTINATION) are uppercase. Variables are mostly uppercase with underscores between words, except for variables with direct relation to any named target — then they have the target name as prefix with no case change, followed with underscore, the rest of variable name is uppercase, e.g. variable holding all sources for target MyUtilityLibrary will be named MyUtilityLibrary_SRCS.

Multi-line calls (i.e. set()) have trailing parenthesis on the same line as last parameter, not on separate line:

set(MyUtilityLibrary_SRCS
    Filesystem.cpp
    IniParser.cpp
    Utility.cpp)

Source and header lists should be ordered alphabetically, subdirectories last, preferably separated by a single empty line.

Prefer to use the new, non-global CMake workflow, so target_include_directories(), target_compile_definitions() and target_compile_options() instead of include_directories(), add_definitions() and modifying the global CMAKE_CXX_FLAGS. When using target_link_libraries(), prefer to specify the PUBLIC|PRIVATE|INTERFACE distinction explicitly.

CMake modules

Project-specific CMake modules (such as various FindPackage.cmake files) are placed in a modules/ directory in the project root and then added to CMAKE_MODULE_PATH in the top-level CMakeLists.txt file:

set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/modules ${CMAKE_MODULE_PATH})

CMake toolchain files are placed either in the toolchains/ submodule or in project root, named after a platform (architecture, standard library...) they target, e.g. MinGW-w64-x86.cmake).

C++ code

File and directory naming

All C++ header and source files are named after the class they contain with no case change. In general this is one class / one function namespace per file, but if the classes are closely related and not very large, you can make an exception (see Corrade/Utility/Debug.h or Corrade/Utility/TypeTraits.h for an example). That might also help with compilation times.

Supporting headers (not directly relevant to the end user), such as various configure.h, visibility.h etc. files, which are used only internally and don't contain any publicly documented APIs should have first letter of the name lowercased to distinguish them from API headers.

Code in the root Corrade namespace is in src/Corrade directory, each subnamespace is in its own subdirectory. Unit tests are in Test subnamespace (and subdirectory), in order to make compilers warn about accidentally defined-but-not-used test cases, the whole test source should be wrapped in an additional unnamed namespace. Implementation details (helper classes etc.) which shouldn't be visible to end users should be in Implementation subnamespace in the same file as the class/function for which the implementation is.

Implementation details which are not referenced from any header should be either in anonymous namespace in source file (see below) or (if referenced from more than one source file, from tests etc.) in Implementation namespace in separate file in Implementation subdirectory. These subdirectories are treated as private implementation, thus they are by default excluded from documentation, their symbols shouldn't be exported and contents of these subdirectories shouldn't be installed anywhere.

File structure

Headers have an strict structure. Include guards are basically include path with slashes replaced with underscores to make refactoring as easy as possible. Directly after them is license block, blank line, then Doxygen file documentation block, blank line and after that the contents of the header. Include guard ending is only #endif, without any additional comments.

Source file have similar structure as headers — first license block, then blank line, then the includes, then blank line, then contents of the file.

#include directives should be organized into separate groups separated with blank line — first header belonging to the particular source file (if the file is source file), then system includes, then includes from the engine itself and then internal includes used only in the particular file (e.g. configure.h). Inter-project includes should be relative to the src/ directory. Includes from system libraries and dependencies are in angle brackets, includes from the library itself are in quotes. Includes should be sorted by "level" (e.g. C includes (<cstdlib>) before C++ includes (<string>) and those before dependencies (<dirent.h>)), then by path name.

Code format

Naming style

Namespaces, classes and structs, enums, type template names and static constants should be named with CamelCase, i.e. first letter of the name and each successive word uppercase, other lowercase. E.g. ConfigurationGroup. Classes with pure virtual methods or base classes which are not meant to be used directly should be prefixed with Abstract.

Functions, function parameters, non-type template names and variables should be named with camelCase, i.e. first letter of the name lowercase, each successive word starting with uppercase. E.g. trimLeadingSpaces().

Preprocessor macros should be written in uppercase with underscores between words, e.g. CORRADE_EXPORT and prefixed with project name. Exceptions are tiny one-shot macros that are #undefined right after they are used (switch case helpers etc.), in that case they are lowercase and prefixed with underscore.

Redundant information and abbrevations in names are not recommended (use determinant, inverse and attribute instead of det, inv and attr). Hungarian notation, I and E prefixes for interfaces and enums are forbidden and cruelly punished, if found. Acronyms should be written with first letter uppercase only to avoid visual collision with preprocessor macros (Http and Sha1 instead of HTTP and SHA1).

Private variables should be prefixed with a single underscore. Don't use the m_ prefix or nothing at all. Don't use two underscores, as they are reserved for internal usage by the C++ standard. Underscores before the name shouldn't be used in public API or function parameters, as it looks ugly in the documentation.

Global and class-static variables shouldn't be used for obvious reasons (static initialization order fiasco, negative effect on startup and shutdown time, thread safety etc.). If there is no other option, try to reduce its scope as much as possible — anonymous namespace or constexpr function-local variable, using CamelCase naming. If the variable is a numberic constant, put it in an typed enum instead. Don't use the g_ prefix.

API usability and naming clarity

Where performance concerns doesn't make it impractical, immutable APIs that return modified data are preferred to APIs that mutate itself. For example, in the following example, the hypothetical matrix class API actually mutates the original instance — similar to GLM's design, in a way:

Matrix 4 a;
a.translate({3.0f, 1.0f, 2.0f});
a.rotate(Deg(35.0));

The reason this is bad is that you need two expressions to create a rotation matrix (first create the instance, then mutate it), or write unintuitive code like Matrix4{}.translate(). Not to mention issues where it's not clear from which side the transformations get combined. To fix that, with an API written in an immutable design, the above would be instead:

Matrix 4 a =
    Matrix4::rotation(Deg(35.0f))*
    Matrix4::translation({3.0f, 1.0f, 2.0f});

Note the different naming — the functions now say "this is what you get on output" instead of "this is what I will do on the instance", and also note the different order — with the above it had to be assumed (or checked in the docs or source code) that translation happens first and rotation after, with this the order is clearly given by how the matrix multiplication happens.

To prevent mutability/immutability confusion, immutable functions shouldn't use verbs. For example the following Containers::String API existed originally and had to be renamed to avoid confusion:

Containers::String a = "not confused!";

a.stripPrefix("not "); // does nothing -- the function actually returns a view

Now it's Containers::String::exceptSuffix() which makes it clearer that the user gets a result back and nothing is mutated.

Forward declarations and forward declaration headers

Each namespace should have a forward declaration header with forward declarations of types that are often used. The header is named after the namespace and is placed inside the namespace folder, so e.g. namespace Corrade::Utility has src/Corrade/Utility/Utility.h header with forward declarations.

Prefer forward declarations to includes as much as possible, as it significantly reduces compilation time. Apart from class and structure forward declarations, put also relevant typedefs in the forward declaration header and make use of C++11 forward-declared enums.

Namespace declarations

In line with vertical code compression, namespace declaration is on one line, separated with blank lines on both sides, closing brackets being all on the same line. The code inside the declaration is not indented to save horizontal space.

namespace Corrade { namespace Utility { namespace Test {

// ...

}}}

Separate the namespace declarations and indent them only if it improves readability and is not excessive, e.g. in case of internal forward declarations:

namespace Corrade { namespace Utility {
    class Configuration;
}}
namespace Corrade {

namespace TestSuite {
    class Tester;

    namespace Compare {
        class FileContents;
    }
}

// the actual code...

}

Class and structure declarations

Class and structure protection levels are ordered as following: first public members, then protected members and finally private members, so class public interface is easy to spot at the top of the file. Friend declarations are preferred to stay in the private section at the end, as they are usually not very important.

Members are ordered as following: first classes and enums, then static functions, then non-static functions (where constructors, destructors and assignment operators are first), then static variables, then non-static variables. Again, classes and methods are more important for user than variables, static members are more important than non-static.

To avoid accidental implicit forward declaration when declaring friends, use the C++11 friend declaration without class/struct keyword (so friend Bar; instead of friend class Bar;).

Blocks, whitespace and indentation

The goal is to preserve readability with minimal amount of wasted vertical and horizontal space.

Indented is everything except namespace declarations (as specified above) — blocks, class protection levels, case statements. Overlong line breaks are indented also with 4 spaces (or any other value if it makes more sense, e.g. for aligning similar code).

class Foo {
    public:
        void bar() {
            addMethods(&Foo::methodNameOne,
                       &Foo::methodNameTwo);
        }

    protected:
        void methodNameOne();
        void methodNameTwo();
};

Whitespace is not allowed inside parentheses and between function name or branch/loop statement and left parenthesis (or between constructed variable name and left brace) and before semicolon. It must be around block braces if the block is not empty. It must not be space before, but after the comma.

if ( statement&&! other ){foo ( a ,b ) ;}  // awful! (_everything_ is misplaced)
if(statement && !other) { foo(a, b); }     // perfect

Whitespace should be around operators with low precedence (namely +, -, <<, >> but not on unary -), around all boolean and comparison operators (&&, ||, ==, !=, <, <=, >=, >), around = and related operators and around ternary operator. Whitespace shouldn't be around /, *, % to indicate they have precedence before addition and subtraction. Whitespace can or need not to be around binary operators (&, |, ^), whichever looks better in particular content.

a += b*5 - 3*(c != 0 ? 64/c : 1);

In general, an indentation style not dependening on particular naming choices should be preferred to avoid nontrivial editing effort when refactoring later. In case of functions, the argument list is replicated in Doxygen code anyway, which is optimized for readability (see below).

// Bad
void fizzBuzzImplementationV3Final(std::ostream&         out,
                                   const std::string&    fizzTemplate,
                                   const std::string&    buzzTemplate,
                                   Util::LoggerInstance& logger);

// Good
void fizzBuzzImplementationV3Final(std::ostream& out, const std::string& fizzTemplate,
    const std::string& buzzTemplate, Util::LoggerInstance& logger);

Block opening braces start on the same line as corresponding statement (class / function declaration, branch and loop statement), else and else if statements have both braces on the same line too:

if(expression) {
    // ...
} else if(another || expression) {
    // ...
} else {
    // ...
}

If the block is only one statement and short enough (<80 columns), it can be on the same line:

void setProcessingDelay(int delay) { processingDelay = delay; }
if(!initialized) return false;

The only exceptions when opening braces are on the following line is when the if statement or constructor initializer list is spanning multiple lines and the other way would confuse what belongs inside the block and what outside. There you can also indent in a way that aligns similar code.

if(a_very_long_expression_spanning_whole_line ||
   another_fairly_lengthy_expression ||
   yet_another_overly_complicated_expression)
{
    // ...
}
MyClass::MyClass():
    _oneMember{lengthy_member_initialization},
    _anotherMember{another_fairly_lengthy_expression},
    _thirdMember{more_code}
{
    // ...
}

The opening brace should be also on the following line when preprocessor branching is involved, as specifying it twice may confuse code folding in some editors:

// Bad
#ifndef CORRADE_TARGET_WINDOWS
if(expression) {
#else
if(worked || around || expression) {
#endif
    // ...
}

// Good
#ifndef CORRADE_TARGET_WINDOWS
if(expression)
#else
if(worked || around || expression)
#endif
{
    // ...
}

Switch statements

Switch statements are usually very verbose and thus they are often subject of various shortening and whitespace tricks. The goal is to make them as readable as possible without wasting too much vertical space, for example:

switch(type) {
    case Type::Byte:        size = 8;   break;
    case Type::Short:       size = 16;  break;
    case Type::Int:         size = 32;  break;
    case Type::Long:        size = 64;  break;
    case Type::LongLong:    size = 128; break;

    default:                size = 0;
}

In case of longer statements or variable declarations inside switch cases that require a block, the style template is as follows:

switch(event.type()) {
    case EventType::KeyPress:
    case EventType::KeyRelease: {
        KeyEvent e{event.type(), event.key()};
        keyEvent(e);
    } break;

    case EventType::TouchSwipe:
        Debug() << "Unimplemented.";
        break;
}

Most compilers are able to warn if a switch doesn't cover all cases, so make use of that feature — if the switch statements enumerated all possible values of given enum, don't put a default statement with fallback code there because that will silence possible warning if someone adds a new enum value without updating the enums:

// Bad
std::size_t size;
switch(type) {
    case Type::Byte:        size = 8;   break;
    case Type::Short:       size = 16;  break;
    // Type::Int missing but the compiler doesn't warn so it fails at runtime
    case Type::Long:        size = 64;  break;
    case Type::LongLong:    size = 128; break;

    default:
        Error() << "Unknown type";
        size = 0;
}

// Good
std::size_t size = 0;
switch(type) {
    case Type::Byte:        size = 8;   break;
    case Type::Short:       size = 16;  break;
    case Type::Long:        size = 64;  break;
    case Type::LongLong:    size = 128; break;
} // warning: Type::Int not handled in switch statement

if(!size)
    Error() << "Unknown type";

Class member and function keywords

Use keywords in this order: template virtual inline constexpr explicit static. When the function is fully defined in class body or when it is marked as constexpr, don't mark it as inline, it's redundant. Only base virtual functions should have virtual keyword, the reimplementations should be marked with override instead.

Preprocessor macros

Preprocessor macros should have the same indentation as surrounding code. Not indenting or indenting between # and the macro breaks visual flow and thus is not allowed. If the macro is long or contains code which should be spanned or multiple lines, it should be wrapped on multiple lines, each successive line indented and the line wrapping indicator \ should be aligned around 80th column, preferably on some tab-stop.

Class constructors and destructors

All constructors (except copy and move constructors) should be explicit by default. Remove the keyword only if you want to allow implicit conversions or want to construct the class with brace initializer (e.g. vector and matrix classes) If the class has no constructor, add explicit default-created one:

class MyClass {
    public:
        explicit MyClass() = default;

        // ...
};

If the constructor is intentionally implicit, mark it with appropriate comment.

If the class has only static members (e.g. various utility classes, traits classes), delete the constructor to prevent instantiating the class by mistake:

class UtilityClass {
    public:
        UtilityClass() = delete;

        // ...
};

If the class is not meant to be instantiated directly and doesn't have any pure virtual methods, consider using protected destructor. Only if that is not possible (because you need to be able to call delete on the Abstract* class), use virtual destructor.

Constant expressions and constants

Use constexpr keyword where appropriate, mainly for getters, operators and static pure functions which will be heavily used with compile-time constants, such as Containers::EnumSet::operator&().

Traits class members and class constants which are not meant to be referenced or pointed to should be defined as constexpr function or using anonymous typed enum (see below), because declaring them as static const variable will result in another (probably unwanted) symbol and the constant might not be always inlined.

Assertions

Use asserts as much as possible. Utility library has convenient CORRADE_ASSERT(), CORRADE_INTERNAL_ASSERT(), CORRADE_INTERNAL_ASSERT_OUTPUT() and CORRADE_ASSERT_UNREACHABLE() macros, see their documentation for more information. When documenting assertions in Doxygen documentation blocks (see below), use the word "expects" to describe a condition that is covered by an assert macro.

Enums and inheritance

Try to avoid having one enum accessible through more than one type, for example:

class AbstractImage {
    public:
        enum class Format { RGB, RGBA };

        void setFormat(Format);
};
class Image2D: public AbstractImage {};
class ImageWrapper: public AbstractImage {};

With this you have more than one way to specify the format (trivial choices slowing down the development), not to mention confusion (are all the enums the same or is each enum different?) and weird combinations:

Image2D image;
image.setFormat(AbstractImage::Format::RGB); // should I prefer `AbstractImage`
image.setFormat(Image2D::Format::RGB);       // or `Image2D`?
image.setFormat(ImageWrapper::Format::RGBA); // is this even allowed?

Better way would be to extract the enum out, now there is only one way to specify the format (and it's less typing). Also, when the user later decides to change the image type to another, the enum value doesn't need to be updated:

enum class PixelFormat { RGB, RGBA };

class AbstractImage {
    public:
        void setFormat(PPixelFormat);
};
class Image2D: public AbstractImage {};
class ImageWrapper: public AbstractImage {};

Image2D image;
image.setFormat(PixelFormat::RGBA);

On the other hand, if each class has its own enum values, the original way is the preferred one:

class ColoredImage {
    public:
        enum class Format { RGB, RGBA };
};
class BWImage {
    public:
        enum class Format { Grayscale, Binary };
};

Initialization

Prefer to use the C++11-style initialization with {} instead of (), as it doesn't have the Most Vexing Parse issue and the compilers are catching a lot more conversion issues that they did with the C++03-style initialization. This goes hand-in-hand with implicit constructors. Example:

MyClass::MyClass(): _oneMember{something}, _anotherMember{otherThing} {}
Vector2 a{0.3f, 11.0f};
std::vector<int> data{5, 1, -3};
int b{}; // zero-init

Virtual functions

Stay away from virtual functions if not needed. If you just want the user to always subclass a type, declare its destructor as protected. Prefer to make the public API non-virtual and make pure virtual functions that shouldn't be called by the subclass private.

Naked pointers

Prefer to use references instead of pointers in function parameters and class members. Use raw pointers only as non-owning optional references, use Containers::ArrayView and Containers::StaticArrayView to pass array references around. If you need to hold or transfer ownership, use Containers::Pointer or Containers::Array. If you need the type that contains a reference to be copyable/movable, use Containers::Reference instead of a reference.

Avoid std::unique_ptr or std::reference_wrapper, as the <memory> and <functional> headers are very heavy, significantly affecting compile time, and neither of these types can be portably forward-declared. The alternative Containers::Pointer and Containers::Reference types are implicitly convertible to these if STL compatibility is needed, see their docs for more information.

Use of std::shared_ptr is banned because, apart from compile-time effects of std::unique_ptr, it involves locking, mutexes for thread-safe reference counting. None of that should be needed in a properly designed API. The only place where its usage is allowed is when needed to interface with 3rd party APIs. In that case the use should be reduced to ideally just a single source file to minimize compile time costs.

SFINAE and templates

Prefer to use std::enable_if on function return type rather than in template argument list, as it is much faster because it doesn't participate in overload resolution (details: https://bugs.llvm.org/show_bug.cgi?id=36158).

Hide verbose SFINAE sequences for Doxygen documentation and replace them with a descriptive template argument name instead, where possible. For example instead of using std::enable_if<std::is_integral<T>::value>::type, simply rename T to Integral for the purpose of documentation.

Discouraged C/C++ features

Don't use RTTI (dynamic_cast and std::type_info) and exceptions, if you don't desperately need to. They violate the C++ principle of "don't pay for what you don't use". Ideally the code should be compilable with -fno-rtti -fno-exceptions.

Heavy STL headers

Don't add heavy STL includes in header files. Each of these #include s brings >20k lines of source code which needs to be preprocessed and compiled even if majority of it is later tossed out. Prefer to include these only in source files. The following headers are the heaviest, numbers taken from g++-4.8 -std=c++11 -E | wc -l:

  • <algorithm> — 46k lines after preprocessing. Most of it are new C++11 features, with C++03 it had only ~10k.
  • <istream> and friends — 22-24k lines after preprocessing. Use <iosfwd> instead and include stream headers only in source files. If you want to just print something to output, use Utility::Debug class and friends.
  • <iterator> — includes stream headers, thus the same as above.
  • <memory> — 25k lines after preprocessing.
  • <random> — 36k lines after preprocessing.

In some cases, Corrade has forward declaration headers that help reduce the bloat in STL headers:

In C++17 and up, #include <cmath> is about 11k lines on libstdc++ due to new math additions such as std::riemann_zeta(). For this reason, directly including the <cmath> header is discouraged and you should be using Corrade/Utility/StlMath.h instead.

using namespace keyword

using namespace std is not allowed. It brings many symbols into default namespace and can lead to unintended behavior. Consider the abs() function. If using it without std:: prefix, you can by mistake use the non-overloaded double abs(double) from plain C <math.h> header, which can cause serious performance issues when using it for integers only. By explicitly calling std::abs() you ensure that proper overload is always selected.

using namespace keyword should be used only for root namespace, i.e. using namespace Corrade and not using namespace Corrade::Utility. If you are using some class from subnamespace in many places, use only that particular name, e.g. using Corrade::Utility::ConfigurationGroup and try to restrict it to as small block of code as possible (e.g. place the using only in function body). In general, where possible, choosing short, descriptive and not too deep name should be preferred to using the using keyword.

Similar guidelines apply to C++11 user-defined literals — restrict the using keyword to as small block of code as possible to avoid confusion:

{
    using namespace std::chrono::literals;
    time = 18h + 5m + 30s; // 18:05:30
}

It's probably common practice, but repeating won't hurt: headers shouldn't have using declarations at all (unless there is good excuse for bringing some useful feature from different namespace into another namespace — but not in the default namespace!).

C-style casts

Don't use C-style casts, use C++-style static_cast and reinterpret_cast instead. Corrade enables compiler warning about their usage in CORRADE_CXX_FLAGS CMake variable. You can also use "constructor cast" instead of static_cast when doing non-harmful conversions, such as integer to float or strongly-typed enum to its underlying type.

int a = 22;
int b = 7;
float pi = ((float) a)/b;           // bad!
float pi = static_cast<float>(a)/b; // good
float pi = float(a)/b;              // even better here

Unscoped and untyped enums

Prefer to use C++11 enum class. Use Containers::EnumSet instead of integral bit masks. Always explicitly specify underlying type, it might save some memory and also allows for forward declaration of the enum, which is good if the enum is part of otherwise large header.

static keyword

Prefer to use anonymous namespaces instead of static keyword. Anonymous namespace ensures that the symbols won't be exported and unlike static keyword it can be used also for type declarations.

Comments

All comments should be in C-style (i.e. slash and asterisk, not two slashes). There should always be space after leading asterisk and before trailing asterisk, this is also the case for C++-style comments, if are used somewhere (mostly in the documentation, because nesting of C-style comments is impossible).

C++-style comments, when used in code, are seen as temporary messages because they require less effort to type (various TODO, FIXME messages etc.).

Multi-line comments should be done in a way which preserves block align (so when first line starts with slash, asterisk and space, the second line should be indented with three spaces to align the text). Successive lines are without leading asterisk to differentiate them from Doxygen comments.

/* One-line comment */
int b = foo();

/* Slightly longer comment about a more complex section of a code that spans
   multiple lines */
int c = b + bar();

Doxygen documentation

Doxygen documentation uses C-style comments with two asterisks, each successive line should start with asterisk and space to further differentiate them from code comments. However, documentation block starting at beginning of the line (with no indentation) and spanning more lines shouldn't have leading asterisks and use whole 80 column width to save horizontal space.

/**
@brief Some class

Documentation block that is not indented is without leading asterisks.
*/
class SomeClass {
    public:
        /**
         * @brief A function
         *
         * Documentation block that is indented has leading asterisks.
         */
        void foo();
};

Doxygen commands are prefixed with @, not \.

Doxygen has an "autobrief" functionality similar to JavaDoc (first line is taken as a brief documentation), but this has several drawbacks and layout/parsing issues so this functionality is not enabled and brief documentation should be always prefixed with @brief and without a period on the end. Due to Doxygen limitation, the brief documentation has to be on a single line.

If the documentation comment is short enough, it can be on one line. If not, the first line should contain only the slash and two asterisks. However, there are exceptions for commands that can be on the first line of the multiline documentation block — namely @page, @name, @file, @namespace and other commands for "out-of-place" documentation.

Doxygen documentation is mainly in header files. In source file there should be only @todo or @bug items related to particular lines of code. Sections inside class/function documentation should be prefixed with corresponding name (with no case change), followed by dash, the rest of the name is in lowercase with dashes between words:

namespace Corrade { namespace Utility {

/**
...
@section Utility-ConfigurationValue-basic-usage Basic usage
...
*/
class ConfigurationValue {

Additional documentation files which don't belong to any particular class (e.g. namespaces, overview...) are in doc/ directory and use *.dox extension. Documentation pages, page sections and documentation files should be named in lowercase with dashes between words, e.g. corrade-coding-style, filename corresponds to name of documentation page it contains.

Each file has a file documentation block (@file) which is right after the license block. It explains only in @brief what content given header contains so the user is able to go to given type documentation right from the file list:

/** @file
 * @brief Class @ref Corrade::Utility::Unicode
 */

Namespace documentation is only in the namespaces.dox file inside doc/ directory. Exception is in case given namespace appears only in one file, is tightly tied to some class or function and does not make sense outside of it. In that case the documentation should be directly in the file.

Class, function and enum documentation block always starts with a @brief description. After that is documentation of template parameters, function parameters and return value. These are documented only if it's not obvious from the description or function/parameter name (i.e. it's not needed for getters and setters). If needed, follows an empty line and paragraphs of longer description, not prefixed with @note or anything (these are used only to further highlight content inside those paragraphs). Last are various @see, @todo sections and other special commands.

It is often the case that you already have some indentation from the left side and you need to fit the documentation into quite a narrow block. Try to use the available horizontal space to the maximum, so indent only as strictly necessary and don't be afraid to make exceptions for overly long identifiers that would otherwise cause excessive blank space. Example:

class Debugger {
    public:
        // ...

        /**
         * @brief Write frame capture into a file
         * @param filename      Filename to write the output into. Format is
         *      autodetected from the extension, supported formats are  `*.png`
         *      and `*.jpg`.
         * @param format        Pixel format
         * @param dataLayoutFlags Data layout flags
         * @return File size on success, `0` on failure
         *
         * Writes a frame capture with currently enabled debug watermark into a
         * file.
         *
         * If the file in given location already exists, the filesystem is not
         * writable or the filesystem is full, returns `0`. The @p dataLayoutFlags
         * and @p format are expected to be compatible with current framebuffer
         * setup, otherwise an assertion is fired.
         */
        std::size_t writeFrameCapture(const std::string& filename, PixelFormat format, DataLayoutFlags dataLayoutFlags);
};

Trivial function overloads (e.g. const overloads) are marked only with @overload keyword and are always after the original documented function. Doxygen is set up to not reorder functions by name, so they will appear in this order also in the generated docs.

/** @brief Data */
char* data() { return _data; }

/** @overload */
const char* data() const { return _data; }

Enum value and trivial C structure documentation is without the @brief section because it only adds unnecessary padding and the brief section does not have any other visual difference in Doxygen output.

If the documentation is short enough, it can be placed after the documented element, but only if it doesn't exceed the 80 character limit too much (e.g. enum value documentation):

enum class Flag: unsigned char {
    Active,         /**< Foo has active bar */
    Inactive,       /**< Foo has no active bars */
    FileNotFound    /**< File was not found */
};

For hiding implementation details from documentation or for working around Doxygen parsing issues (and false positive warnings), preprocessor define DOXYGEN_GENERATING_OUTPUT can be used. It is defined only when Doxygen generates the documentation. If that works around some Doxygen parsing bug, don't forget to mark it with @todoc (see below). Example:

/** @brief Set new value */
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class T> void set(T value);
#else
template<class T> type std::enable_if<std::is_convertible<T, int>::value, void>::type set(T value);
#endif

Code blocks using the \@code command should be separated from surrounding text with a blank line and every code block should specify the language so highlighting is done properly:

/**

A paragraph of text.

@code{.cpp}
// C++ code
@endcode

Another paragraph of text.

@code{.cmake}
// CMake code
@endcode

*/

See below for a way to highlight inline code snippets. The inline code is detected using a heuristics and due to Doxygen XML output limitations, sometimes a single-line code block is detected as inline code and prepended to the immediately following paragraph. In order to prevent that, add a stray <p> tag right after the block:

/**
Text paragraph before a code block.

@skipline foo
<p>

Next text paragraph after a code block.
*/

Section ordering

In detailed documentation the text should be always first, additional blocks are then ordered by their importance. Various @note, @attention and @warning blocks to highlight some information are always first, then @see block with links to related stuff, then various support information such as @partialsupport, after that @deprecated information and @todo, @todoc and @bug always last, as they are the least important and in most cases only for internal use.

Special documentation commands

All @m_* commands from the m.css theme are available.

Code

It's possible to annotate inline code with language as well. There's no difference in the output for the stock Doxygen HTML output, but the m.css theme can make use of it. The @cb command denotes beginning of inline code snippet and takes the language as an argument. Inline code end is denoted with @ce. To make things easier, there are @cpp and @cmake aliases that expand to @cb{.cpp} and @cb{.cmake}, respectively.

/** Returns @cpp Magnum::Vector2 @ce, which is @cb{.glsl} vec2 @ce in GLSL. */

Documentation-related TODOs

TODOs about documentation (or about Doxygen workarounds) should be written with @todoc instead of @todo. They will appear on separate page.

Debugging operators

Operators for printing types on Corrade's debug output should be marked with @debugoperator, e.g.:

/** @debugoperator{Hash} */
Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, const Hash& value) {
    // ...
}

They will appear as related functions within documentation of class specified as the parameter and also crossreferenced from the Debug output operators for custom types page.

For non-class enums there is @debugoperatorenum command and for in-class enums the @debugoperatorclassenum command:

enum class Flag: std::uint32_t;

/** @debugoperatorenum{Flag} */
Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, Flag value);

class Foo {
    enum class Flag: std::uint32_t;
};

/** @debugoperatorclassenum{Foo,Foo::Flag} */
Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, Foo::Flag value);

Debug operator for non-class enum will appear in documentation of surrounding namespace, debug operator for class enum will appear as related function in documentation of given class.

Configuration value parsers and writers

Helper classes for parsing and writing Utility::Configuration values should be marked with @configurationvalue, and referenced from the class using @configurationvalueref e.g.:

/** @configurationvalueref{Hash} */
class Hash;

/** @configurationvalue{Hash} */
namespace Corrade { namespace Utility {
    struct ConfigurationValue<Hash> {
        // ...
    };
}

They will appear linked with documentation of class specified as the parameter and also crossreferenced from the Configuration value parsers and writers for custom types page.

Tweakable literal parsers

Utility::Tweakable literal parsers should be marked with @tweakableliteral, similarly to how it's done for configuration value parsers. They will appear linked with documentation of the class specified as a parameter and also crossreferenced from the Tweakable literal parsers page.

Partially supported features

Features that are not supported on all platform should be marked with @partialsupport, listing unsupported platforms and optionally reason why. They will be all crosslinked through the List of partially supported features page.

Third party dependency licensing info

Libraries, plugins and executables that make use of third party code should be visibly marked with @thirdparty, in a color-coded block according to the description in Third-party components and highlighting the license name as well. It should link to the original license text, ideally also to a matching overview on https://choosealicense.com and have a short sentence explaining the main requirements (e.g., whether it's attribution, source code release, dynamic linking, explicit licensing for commercial use etc.) Example:

/**
@m_class{m-block m-success}

@thirdparty This plugin makes use of the [HarfBuzz](http://harfbuzz.org/)
    library, licensed under @m_class{m-label m-success} **MIT**
    ([license text](https://raw.githubusercontent.com/behdad/harfbuzz/master/COPYING),
    [choosealicense.com](https://choosealicense.com/licenses/mit/)). It
    requires attribution for public use.
*/

Backwards compatibility and experimental features

Common practice is to keep backwards compatibility when refactoring APIs — if given feature is present for a considerable amount of time or is already part of some release, any change that breaks source compatibility should be complemented with backwards-compatible function or type alias that is marked as deprecated using @deprecated and one of CORRADE_DEPRECATED() macros. If given feature is still in experimental stage, maintaining backwards compatibility might hurt innovation — in that case the feature should be marked with @experimental.

Git

Preferred workflow is linear with clean and atomic commits to make history browsing and reviews easier. The commits should not include random whitespace changes, because it breaks the git blame tool. Big textual files that are treated as binary (SVG...) should be marked with -diff in appropriate .gitattributes files to avoid excessive changes.

The repositories should not contain editor and IDE-specific configuration files and the global .gitignore should not include platform-specific files that can be ignored on a system-wide level (such as .DS_Store or desktop.ini).

Commit message format

Each commit message should have short one-line explanation at the beginning, formed as a sentence (i.e., first word capitalized and period at the end). If the commit affects only some submodule (namespace...), module name should be prepended and separated from the (uncapitalized) rest of the sentence with colon (and space). Detailed example:

ModuleName: short one-line summary ending with a period.

More detailed explanation text, if necessary, wrapped on multiple lines,
each line at most ~72 characters long to avoid ugly wrapping in git log.

 - Bullet points, indented with one space before and one space after the
   bullet, indented to have the text aligned. Colons are allowed too, but
   not mixed with bullets.
 - Bullet points are separated from surrounding text with blank lines.

It is good to use *emphasise*, _underline_ or `code` as in Markdown, if
necessary, but they shouldn't be overused.

    Multi-line code examples are indented with four spaces, as in Markdown
    syntax.

Repository, branch and tag format

Groups, repositories, branches and tags should be named in lowercase with words separated with colons (-), e.g. physics-performance-tweaks. Version tags start with v, followed by version number, e.g. v1.0.3.