All ctools C++ code must be compatible with ISO standard C++98. Do not use C++11 features (see here if you don’t know what C++98 and C++11 is and note that C++03 is just a minor correction of C++98).
The reason for this restriction is that C++ compilers / standard libraries such as gcc / libstdc++ or clang / libc++ only finished implementing C++11 in 2013 and using C++11 features in ctools would make it impossible to use the code on many systems.
Each function or method starts with a curly bracket in the line following the function or method name. The return type is in the same line as the function or method name, e.g.:
int function(void)
{
int i = 0;
...
return i;
}
Code blocks should be encompassed in curly brackets, even if the block consists only of a single line.
The opening curly bracket of a block starts in the same line as the related statement, e.g.:
for (int i = 0; i < 10; ++i) {
sum += i;
}
Separate code elements by spaces, e.g.:
Yes: int i = 0;
No: int i=0;
Align successive class definition on the member function name, e.g.:
void log10GeV(const double& eng);
void log10TeV(const double& eng);
std::string print(void) const;
Prefix data members with m_, e.g.:
m_num
m_response
m_grid_length
m_axis_dir_qual
Each class shall have the protected methods init_members, copy_members, and free_members to handle initialisation, copying, and eventually freeing of allocated memory for data members:
// Initialise data members
void Class::init_members(void)
{
m_elements = 10;
m_array = new double[m_elements];
for (int i = 0; i < m_elements; ++i) {
m_array[i] = 0.0;
}
...
}
// Copy data members
void Class::copy_members(const &Class class)
{
m_elements = class.m_elements;
m_array = new double[m_elements];
for (int i = 0; i < m_elements; ++i) {
m_array[i] = class.m_array[i];
}
...
}
// Free data members
void Class::free_members(void)
{
if (m_array != NULL) delete [] m_array;
...
}
Each class shall have at least a void constructor, a copy constructor, a destructor and an assignment operator. Additional constructors and operators can be implemented as required. The following example shows the basic implementation for these 4 methods. Due to the usage of the init_members, copy_members, and free_members, most classes will have exactly this kind of syntax:
// Void constructor
ctnice::ctnice(void)
{
init_members();
return;
}
// Copy constructor
ctnice::ctnice(const ctnice& nice)
{
init_members();
copy_members(nice);
return;
}
// Destructor
ctnice::~ctnice(void)
{
free_members();
return;
}
// Assignment operator
ctnice& ctnice::operator=(const ctnice& nice)
{
if (this != &nice) {
free_members();
init_members();
copy_members(nice);
}
return *this;
}
Do not use macros.
Do not use #define directives for the declaration of constants. Use const instead.
Do not use std::strncpy, std::memcpy or similar as these functions are corrupted on some systems.
If possible, pass arguments by reference.
Output arguments should be passed as pointers.
Use C++ (std::string) instead of C-style (char*) strings.
Use C++ casts instead of C-style casts.
Avoid using templates.
Do not use an integer for a floating point argument (i.e. write 10.0 instead of 10). Some older compilers give an error when using integers in some floating point functions, such as log10().
Where possible (and appropriate), use std::vector containers instead of allocating memory. In other words: avoid direct memory allocation with new.
Use the std:: namespace prefix where possible; write for example
std::sin(angle);
std::cos(angle);
You may not believe it, but droping the std:: may on some systems lead to considerably slower code for trigonometric functions!
Use explicit for constructors with single arguments to prevent unintended type conversions. The only exception to this rule is the copy constructor or type conversion constructors.
Specify void for function or method definitions without arguments, e.g.:
Yes: void function(void)
No: void function()
Use pre-incrementation in loops (pre-incrementation is faster than post-incrementation), e.g.:
for (int i = 0; i < 10; ++i) {
sum += i;
}