The purpose of a coding style and accompanying guidelines is to make the reading and comprehension of the code easier. Reading prose would also be much harder, should the punctuation rules differ from chapter and paragraph to the next.
MaxScale comes with an uncrustify configuration file that can be used to format the source code. To use it, run the following in the source root.
uncrustify -c uncrustify.cfg <path to source>
This will format the source file according to the MaxScale coding style.
Note though that the purpose of uncrustify is to be an assistant and not the master. That is, if its formatting does not look good, then manually make it look good and exclude the section using
/* *INDENT-OFF* */
int i = 0; // No uncrustify action here
/* *INDENT-ON* */
Be consistent. In a particular context, everything should style wise look the same.
If something is done in a particular way in some context, then follow that same way when doing modifications, even if it violates what is stated here. If you want to fix the style of a file, then do so in one separate change; not as part of other modifications.
- Only spaces, no tabs.
- Indentation depth 4 spaces.
- No trailing white space.
- Maximum line length 110 characters.
- In general, comment only what is not obvious from the code.
- APIs intended to be implemented by components independent of the API
itself should be documented properly. Use
include/maxscale/filter.hh
andinclude/maxscale/router.hh
as example. - On a particular line, the number of characters in documenting text should not exceed 80, for ease of reading. When counting the characters, any prepending code or space, due to indentation, is not included. The hard line length limit of 110 still applies.
/*
* Text consisting of 33 characters.
*/
struct S
{
int field; // Text consisting of 33 characters.
};
void f()
{
if (this)
{
if (that)
{
// Text consisting of 33 characters.
...
}
}
}
We follow the Allman indentation style.
Braces are always used and the brace associated with a control statement is placed on the next line, indented to the same level as the control statement. Statements within the braces are indented to the next level.
if (something)
{
do_this();
}
else
{
do_that();
}
if (something)
{
...;
}
while (something)
{
...;
}
Exceptions are sizeof
, typeof
and alignof
.
some_function();
Use one space around (on each side of) most binary and ternary operators, such as any of these:
= + - < > * / % | & ^ <= >= == != ? :
but no space after unary operators:
& * + - ~ ! sizeof typeof alignof __attribute__ defined
no space before the postfix increment & decrement unary operators:
++ --
no space after the prefix increment & decrement unary operators:
++ --
and no space around the .
and ->
structure member operators.
x = some_function(a, b, c + d);
Opening parenthesis and square bracket are not followed by a space, and closing parenthesis and square bracket are not preceded by a space.
int len = strlen(name);
a = b[5];
- snake_case: All lowercase and words are separated by underscores.
- camelCase: The first letter of the first word is lowercase, and the first letter of each subsequent word is capitalized and words are not separated by underscores.
- PascalCase: The first letter of every word is capitalized and words are not separated by underscores.
Naming convention: snake_case
namespace maxscale
{
class ...
}
...
namespace xyz_filter
{
...
}
Note that symbols within a namespace are not indented. Note also that the
namespace maxscale
can only be used by classes belonging to the MaxScale
core. An exception is when a template in the MaxScale namespace is
specialized for a non MaxScale core class.
Naming convention: PascalCase
There is no functional difference between structs and classes, but a type
that primarily contains member variables that are accessed directly by
others should be a struct
and a type that provides behavior should be a
class
.
class CacheFilterSession
{
public:
int some_member_function();
...
};
Naming convention: snake_case
void set_color(...);
Note that the functions in MaxScale's plugin interfaces are exceptions; they use camelCase.
Naming convention: PascalCase
The values must be uppercase.
namespace graphics
{
enum class Color
{
RED,
GREEN,
BLUE
};
}
Naming convention: snake_case
Further, member variables are prefixed with m_
and static member variables
are prefixed with s_
.
class MyClass
{
...
private:
int m_size;
static int s_max_size;
};
In general, a class should have no public member variables but all access should be via public functions that can be inline.
Member variables that are public must not have a prefix.
The following prefixes are "standardized" and can be used when applied consistently:
int *pAge; // p, for pointers.
shared_ptr<NiftyBuffer> sBuffer; // s, for smart pointers
unsigned nItems; // n, when a variable denotes a count.
Note that there is no underscore between the prefix and the actual variable name, and that the character following the prefix is in uppercase. Prefixes can be stacked.
class SomeClass
{
private:
int* m_pIndex;
int** m_ppIndex;
shared_ptr<SomeClass> m_sNext;
};
These are suggestions.
Rationale: With multiple exit points it is very easy to overlook the release of some resource in one of those places.
Reasonable exceptions to the single exit point rule are:
- Checking of preconditions at the beginning of the function.
int function(int a, int b)
{
if (a < 0)
{
return -1;
}
if (b > 5)
{
return -1;
}
int rv;
...
return rv;
}
- Functions that are basically only big switches.
char* toString(enum Color c)
{
switch (c)
{
case RED:
return "RED";
case GREEN:
return "GREEN";
default:
return "Unknown";
}
}
Rationale: In typography the optimal line length for readability is considered to be around 60 characters. With 5 levels of nesting of 4 spaces each, that gives 80 characters. If it is hard to stay within that limit, consider refactoring the function.
if (a != b && !ready()) ...;
if ((a != b) && !ready()) ...;
Rationale: There can never be any doubt about what the intended precedence is.
Rationale: That way it is possible with one glance to get a grasp of what the function does. If it's hard to keep within the limit it may be a sign that the function ought to be refactored.
Rationale: Makes the reading easier.