Code style guide
General rules
Base rules
The base rules are in Google C++ Style Guide: https://google.github.io/styleguide/cppguide.html
All rules below replace the corresponding base rules.
For any subject not mentioned below, please refer to the base.
Commit message
A correct commit message must therefore be structured as:
[NAME_OF_MODULE_OR_PLUGIN] ACTION message
where ACTION includes ADD, REMOVE, FIX, CLEAN, REVERT.
Example: [SofaKernel] ADD test for the mass conservation in UniformMass
Naming
Identifiers must respect the following conventions:
- [N1] Class names are in UpperCamelCase
- [N2] Function names are in lowerCamelCase()
- [N3] Namespaces names are in lowercase
- [N4] Variables names are in lowerCamelCase and must express their use more than their type. Exceptions: mathematical objects like
Matrix M
, local iterators variables likei
,j
,k
and usual symbols likex
for positions orv
for velocities. - [N5] Data member variables names must begin with
d_
- [N6] Link member variables names (e.g. SingleLink) must begin with
l_
- [N7] Other member variables names must begin with
m_
(exception: this is not mandatory for PODs as well as public attributes) - [N8] Names for booleans variables must answer a question:
m_isRed
,m_hasName
- [N9] C++ files must must have the extension .h, .cpp, or .inl
- [N10] Files that define a class should have the same name as the class and should contain only one class.
- [N11] Each library name should be prefixed with
Sofa
.
Formatting
- Special characters like TAB and page break must be avoided.
- Indentation must use 4 spaces everywhere (C++ and Python), but there must be no indentation for namespaces
- Braces use the Allman style: the opening brace associated with a control statement is on the next line, indented to the same level as the control statement, and statements within the braces are indented to the next level.
-
A space character should be used in the following situations:
- After C++ reserved words:
if (true)
- Around binary operators:
a + b
- After commas:
doSomething(a, b, c)
- After semicolons in for statements:
for (unsigned int i = 0; i < container.size(); i++)
- After C++ reserved words:
-
Template declarations are split on two lines
Commenting
Code must be commented in a Doxygen compliant way.
Most used commands are ///
or /**
before blocks and ///<
after member declarations.
Please DO NOT use //
or ///
after member declarations.
Example
/**
* @brief MechanicalObject class
*/
template <class DataTypes>
class MechanicalObject : public sofa::core::behavior::MechanicalState<DataTypes>
{
Data< bool > d_showObject; ///< Show objects. (default=false)
Data< defaulttype::Vec4f > d_color; ///< drawing color
Data< bool > d_isToPrint; ///< ignore some Data for file export
/// @name New vectors access API based on VecId
/// @{
virtual Data< VecCoord >* write(core::VecCoordId v);
virtual const Data< VecCoord >* read(core::ConstVecCoordId v) const;
virtual Data< VecDeriv >* write(core::VecDerivId v);
virtual const Data< VecDeriv >* read(core::ConstVecDerivId v) const;
/// @}
/** \brief Reorder values according to parameter.
*
* Result of this method is :
* newValue[ i ] = oldValue[ index[i] ];
*/
void renumberValues( const sofa::helper::vector<unsigned int> &index );
/// Force the position of a point (and force its velocity to zero value)
void forcePointPosition( const unsigned int i, const sofa::helper::vector< double >& m_x);
/// src and dest must have the same size.
/// Performs: dest[i][j] += src[offset + i][j] 0<= i < src_entries 0<= j < 3 (for 3D objects) 0 <= j < 2 (for 2D objects)
/// @param offset the offset in the BaseVector where the scalar values will be used. It will be updated to the first scalar value after the ones used by this operation when this method returns
virtual void addFromBaseVectorSameSize(core::VecId dest, const defaulttype::BaseVector* src, unsigned int &offset);
}
Good practices
- [G1] You should try to use as few
#include
directive as possible. - [G2] You should limit as much as possible the amount of code in included files (.h, .inl).
- [G3] All definitions should reside in source files. The header files should declare an interface only.
- [G4] Variables should be initialized when they are declared.
- [G5] You should use
const
profusely. - [G6] You should use
assert
profusely. - [G7] You must avoid the
using
directive in header files (.h and .inl): ~~using namespace foo;
~~ - [G8] You should declare automatic variables only when you need them (not before).
- [G9] You must always initialize pointers, either to the address of something, or to
nullptr
- [G10] You may use the type specifier
auto
(since C++11) when:- you are in a for loop
- you deal with iterators
- you deal with long typenames AND or when the variable type is obvious
SOFA specific rules
- Tricky code should not be commented but rewritten! In general, the use of comments should be minimized by making the code self-documenting by appropriate name choices and an explicit logical structure.
- The use of magic numbers in the code should be avoided. Numbers other than 0 and 1 should be declared as named constants instead.
- The definition of ε-definition of limit ('epsilon', an arbitrarily small positive quantity) should not be code specific but rather use the class template
std::numeric_limits
. Here is an example:const DataTypes::Real EPSILON = std::numeric_limits<DataTypes::Real>::epsilon();
const DataTypes::Real EPSILON = std::numeric_limits<DataTypes::Real>::epsilon();
- All internal data, needed by your component, and that can’t be recomputed must be put inside a
Data
or aDataPtr
. This way, your component can be saved. Also, thisData
will be automatically displayed inside the GUI. - For messaging in SOFA components, the dedicated Messaging API must be used.
msg_info()
will display the message in the console only if theprintLog
flag is activated.msg_warning()
will display the message in the console with an warning message.msg_error()
will display the message in the console with an error message.
- Use
sofa::type::vector
instead ofstd::vector
- Only use
sofa::simulation::tree::GNode
when you need to directly use access to the children or the parent of the node. If not, use the more genericsofa::simulation::Node
- When an information, a function or an internal variable from an external component is needed in a component, prefer an explicit Link to connect both components instead of implicitly browsing the graph using
getContext