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:
- LLVM: http:/
/ llvm.org/ docs/ CodingStandards.html - Qt: http://qt-project.org/wiki/Qt_
Coding_ Style - Chromium: http:/
/ dev.chromium.org/ developers/ coding-style/ cpp-dos-and-donts
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/
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 #
undef
ined 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::
Containers::String a = "not confused!"; a.stripPrefix("not "); // does nothing -- the function actually returns a view
Now it's Containers::
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 friend
s, 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::
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_
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::
Avoid std::<memory>
and <functional>
headers are very heavy, significantly affecting compile time, and neither of these types can be portably forward-declared. The alternative Containers::
Use of std::
SFINAE and templates
Prefer to use std::
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::-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:
- Corrade/
Utility/ StlForwardString.h forward-declares std:: string where possible - Corrade/
Utility/ StlForwardTuple.h forward-declares std:: tuple where possible - Corrade/
Utility/ StlForwardVector.h forward-declares std:: vector where possible
In C++17 and up, #include <cmath>
is about 11k lines on libstdc++ due to new math additions such as std::<cmath>
header is discouraged and you should be using Corrade/
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::
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::@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::@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:/
/** @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_@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
.