CODING STYLE OF THE KERNEL
IDE USERS
For CLion users you can import the coding guidelines from here: MercuryCodingStyle.xml
THE GUIDELINES
General Recommendations
Any violation to the guide is allowed if it enhances readability or if it significantly improves the speed of the code.
Most conventions are based on the coding guidelines from http://geosoft.no/development/cppstyle.html
Naming Conventions
File names must be in mixed case starting with upper case, i.e. "FileName".
Directory names must be in mixed case starting with upper case, i.e. "DirectoryName".
Names representing types must be in mixed case starting with upper case, i.e. "TypeName".
Variable names must be in mixed case starting with lower case, i.e. "variableName".
Named constants (including enumeration values) must be all uppercase using underscore to separate words, i.e. MY_CONSTANT.
Names representing methods or functions must be verbs and written in mixed case starting with lower case, i.e. "functionName".
Names representing Classes must be in mixed case starting with upper case, i.e. "ClassName".
Names representing template types should be a single uppercase letter.
Private class variables should have "_" suffix.
Never use identifiers which begin with one or two underscores (`_' or `__'). In most cases those variables are used in imported libraries.
Choose clear variable names that suggest the usage (e.g. BaseParticle* p, Mdouble interactionRadius).
Plural form should be used on names representing a collection of objects.
The prefix "n" should be used for variables representing a number of objects.
The suffix "Id" should be used for variables representing an entity number.
The prefix "is", "has" (for nouns) or "do" (for verbs) should be used for boolean variables and methods.
Negated boolean variable names must be avoided.
Files
C++ header files should have the extension ".h".
Source files should have the extension ".cc".
Driver files should have the extension ".cpp".
Implementation files of templated classes should have the extension ".hcc" and be included at the end of the regular header file.
Large source files can be split in multiple files by using the same name, then an "_" and a description of its usage.
Whenever possible, class should be declared in a header file and defined in a source file where the name of the files match the name of the class.
All definitions should reside in source files, unless they short approximately 3 lines and hence you want them inlined.
Special characters like TAB and page break must be avoided. Most editors have the option to expand tabs into spaces, so that the TAB key can still be used.
Header files must contain an include guard. The name convention resembles the location of the file inside the source tree. (all capitals i.e. WALL_STRAIGHTWALL_H)
Include statements must be located at the top of a file only; except, for template class implementations.
Use " " to include internal Mercury files and use <> to include files from the C++ standard libraries. Full paths do not need to be specified.
Examples: #include "Mercury3D.h", #include<iostream>.
Unit Tests, Self Tests, Demos And Tutorials
To ensure that future updates to the code don't break existing features, MercuryDPM has a suite of test codes that each new commit to has to pass. You can run the test suite by calling "make fullTest" in your build directory. The test suite first compiles all codes in the Drivers directory, then executes the test codes.
Each kernel feature has to be tested to ensure future updates to the code don't break existing features.
Users are encouraged to write tests for their own codes to detect quickly if an update to the Kernel caused a change in the USER code.
There are 3 types of tests, which are recognizable by the ending of the *.cpp* file name:
UnitTest: All codes whose file name ends with UnitTest.cpp are recognised by the test suite as unit tests. These tests should be placed either in Drivers/UnitTests or in your user directory. Unit tests should run in 1 second or less and have internal checks if the output is valid (see for example Drivers/UnitTests/AdhesiveForceUnitTest.cpp). The check should not require comparison with data files.
SelfTest: All codes whose file name ends with SelfTest.cpp are recognised by the test suite as selt tests. These tests should be placed either in Drivers/SelfTests or in your user directory. Those tests should run in 10 seconds or less and create output files that are checked against gold data.
To create your own self test: Let's assume you developed a new Driver, for example Compression.cpp in MercurySource/Drivers/USER/JohnDoe, and you want to create a test to ensure that any new Kernel features won't affect the results of your code. To do so, Create a new code CompressionSelfTest.cpp in the same directory that runs in 10 seconds or less and creates an output file (e.g. CompressionSelfTest.restart) you would like to test against. Please keep the size of this output file as small as possible. Compile and execute your new self test and copy the CompressionSelfTest.restart to the directory MercurySource/Drivers/USER/JohnDoe/SelfTestData (create the directory if it doesn't exist yet). Now use cmake . in the MercuryBuild directory to update the Makefiles and make fullTest to check that all tests (including the newly created one compiles) Use svn add to add the new files to the repository and commit (see Committing your code to SVN).Demo: All codes whose file name ends with Demo.cpp are recognised by the test suite as demos. Most of these tests reside in Drivers/MercurySimpleDemos. These are codes that run in 5 minutes or less, that illustrate a code feature. No special checks are done for Demo codes.
Before any new feature is committed, 'make fullTest' has to be run and return a positive result (see Committing your code to SVN). Any commit that breaks one or more tests is a violation of the MercuryDPM coding standard, and is punishable by a round of beers to all other developers. Note that tests can also be run individually, e.g. use 'ctest -R FreeFallUnitTest' to run FreeFallUnitTest
Statements
The parts of a class must be sorted public, protected and private, however typedefs must be on the top of the file.
Within the parts methods should go before variables.
Use the "explicit" keyword for single argument constructors.
Type conversions must always be done explicitly, never rely on implicit type conversion.
For every "new", there must be a "delete" in the same class. If the "delete" is in another class, it must be documented carefully with both the "new" and "delete".
Variables must never have dual meaning.
Multiple variables should not be declared on the same line.
Use of global variables should be minimized.
Class variables should never be declared public.
Variables should be declared in the smallest scope possible.
C++ pointers and references should have their reference symbol next to the type rather than to the name. (i.e. BaseParticle* p)
Implicit test for 'false' should not be used other than for boolean variables, e.g. 'if(isFixed)' when isFixed is a boolean, but 'if (p == nullptr)' if p is a pointer.
Only loop control statements must be included in the for() construction.
Range based for-loops are preferred over other types of for-loops. For example, a for-loop over vector vec can look like for ( double& d: vec) { }.
Loop variables should be initialised immediately before the loop.
Do-while loops can be avoided.
The use of break and continue in loops should be avoided, go for readability. In these cases, a small function with a return is usually more readable.
The form while (true) should be used for infinite loops.
In cases of an if-statement, the nominal case should be put in the if-part and the exception in the else-part.
Floating point constants should always be written with decimal point and at least one decimal. (i.e. x = 0.0).
Use nullptr instead of NULL or 0.
If a smart pointer is necessary, use unique_ptr or shared_ptr.
Use the type unsigned int for non-negative integer variables.
The "auto"-keyword for type declaration is only allowed if it is instantly clear what the type is.
When using a template, use typename T and NOT class T.
Avoid using "typename" as a return specifier.
Try to avoid "typedef" for type definitions. Use the "using"-keyword if either the type is likely to change, or the typename is very long. This should be done in the class declaration and not globally.
Avoid returning by final function argument (for example void myFun (int a, int b, Matrix& mat) for computing mat).
Layout
Basic indentation should be 4 spaces. Empty lines should be indented.
Class declarations should have the following form:
// space before and after ':' as seen below
class SomeClass : public BaseClass
{
public: // not indented
statements;
protected: // not indented
statements;
private: // not indented
statements;
};
Method definitions should have the following form:
// no space between someMethod and ()
void someMethod()
{
statements;
}
A for statement should have the following form:
// space between for and ()
for (initialization; condition; update)
{
statements;
}
A while statement should have the following form:
// space between while and ()
while (condition)
{
statements;
}
The if-else class of statements should have the following form:
// space between if and ()
if (condition)
{
statements;
}
else
{
statements;
}
A switch statement should have the following form:
switch (condition)
{
case ABC :
statements;
// Fallthrough
case DEF :
statements;
break;
default :
statements;
break;
}
Single statement if-else, for or while statements can be written without brackets, but have to be on two lines.
The function return type can be put in the left column immediately above the function name.
void
MyClass::myMethod()
{
statements;
}
White spaces
Conventional operators should be surrounded by a space character.
C++ reserved words should be followed by a white space.
Commas, semicolons and colons should be followed by a white space.
Semicolons in for statements should be followed by a space character.
Variables in declarations can be left aligned.
Use alignment wherever it enhances readability.
Doxygen
All MercuryDPM documentation is created via Doxygen. Each class or namespace, and all its member functions and variables should be documented by prepending a doxygen comment before its declaration or definition.
All Doxygen style comments must have the following form:
/*!
* Doxygen comment.
*/
For every class, there must be both a brief and a detailed Doxygen documentation in the header file (.h).
/*
* \brief A brief description of the MyClass.
* \details A detailed documentation of MyClass.
*/
MyClass
{
statements;
}
For every member function or variable , there must be brief (\brief) Doxygen documentation in the header file (.h).
MyClass
{
/*!
* \brief A brief description of myFunction.
*/
bool myFunction(int name);
}
The detailed (\details) Doxygen documentation must go into the implementation file (.cc/.hcc). All in- and output parameters must be documented there as well.
/*!
* \details A detailed documentation of myFunction.
* \param[in] name Description of the parameter.
* \return Description of the return value.
*/
bool MyClass::myFunction(int name)
{ ... }
If you find a bug, please mark it with \bug and a description of the bug in Doxygen style.
/*!
* \bug This function does not work for non-spherical particles.
*/
If you think something has to be done in the future at that piece of code, document it in Doxygen style with \todo in front.
/*!
* \todo This function has to be extended to account for ...
*/
If you want a link in the documentation to another class, function, or Doxygen page, use the \ref command.
/*!
* See \ref BaseWall::setPosition for details.
*/
Python files
All Python files in the MercuryDPM repository should use the Python style guide: https://peps.python.org/pep-0008/.
Submodule-related files
All source files that extend the functionality of a submodule (such as oomph-lib) should be written in the submodule's coding style.
Code that interface between MercuryDPM and a submodule should be written in the MercuryDPM coding style.
CMake
Driver level CMakeList.txt file should follow the template from the Template directory.
Customisation to the template should be avoided, if possible.
Files should not be copied from source to build in the CMakefiles, as it confuses users which one is current and sometimes their file gets replaced in the copy. Instead include the header #include <CMakeDefinitions.h> and use the function getMercuryDPMSourceDir()
The one except is when the copy is used to setup a demo; however in this case the file being copied should be stored in the configuration directory
Throw an error if a dependency for a turned on option is not met (e.g. Triangle wall correction does not work with openMP)
FIND_PACKAGE() is called only in root Source/CMakeLists.txt and the Source/CMakeModules directory
INCLUDE() directives, if needed, must come early in the file
ADD_SUBDIRECTORY() directives, if needed, must be done just after INCLUDE
Use builtin WIN32 variable instead of WINDOWS variable to detect a Windows platform. Avoid CYGWIN as it is no longer supported: if(WIN32 AND NOT CYGWIN)...
all install paths should be relative
Create MACRO in *.cmake to factorise some repetitive lines. (Try to) comment them.
Project names should be very specific in their naming to avoid duplicates. (e.g. MercuryDPM instead of just Mercury)
Self-defined CMake variables should begin with the project name + underscore (e.g. MercuryDPM_) and a descriptive text in capital letters
CMake variables which are pushed to the code should be all in capital letters (e.g. MERCURYDPM_USE_MPI and not MerucryDPM_USE_MPI since that would be a CMake internal variable to e.g. enable the code variable MERCURYDPM_USE_MPI)