-
Notifications
You must be signed in to change notification settings - Fork 6
styleguide
Here we describe our preferred coding style for L4Re and the microkernel. The structure and some of the rules are derived from the Linux kernel coding style. In general a common coding style is about maintaining readability and maintainability of a shared code base.
We don't use tabs but spaces for indentation. A new block of control is indented by 2 spaces. To avoid double-indentation in switch statements it is allowed to align the { and the case labels in the same column.
switch (action)
{
case 1:
do_something();
break;
case 2:
{
do_another_thing();
break;
}
default:
break;
}
Indentation with multi-line expressions:
l4_ipc_send(env->main_thread().cap(), l4_utcb(), l4_msgtag(0, 0, 0, 0),
L4_IPC_NEVER);
Namespaces must be started using:
namespace Xyz {
or
namespace Xyz
{
Multiple namespaces shall be started on a single line if they are closed on a single line:
namespace Xyz { namespace Abc {
...
}} // namespace Xyz::Abc
The contents of a namespace must be indented by two spaces (except when covering a whole file):
namespace Xyz { namespace Abc {
This_is_some_statement;
}} // namespace Xzy::Abc
The contents of a namespace shall not be indented when the namespace covers a complete file, this means the file may contain only preprocessor directives before and after the namespace declaration:
#pragma once
#include <cstdio>
namespace X {
Code;
...
}
It is discouraged to use statements like using L4Re::chksys;
or using L4Re::chkipc;
even if you intent to save space: With the namespace in front
of these global functions it is quite clear that this is the global function
and not some local overloading.
Our line length limit is 80 columns. Statements longer than 80 columns should be broken into sensible chunks. One exception to this rule is to never break user-visible strings because that breaks the ability to grep for them.
It is not allowed to put multiple statements on a single line. It is not allowed to put multiple assignments on a single line. The only exceptions is for switch statements where case, statement and break may go on a single line provided the line remains shorter than 80 characters.
switch (foo)
{
case 1: bar = "spring"; break;
case 2: bar = "summer"; break;
case 3: bar = "fall"; break;
default: bar = "winter"; break
}
A single statement that follows an if-condition is put on its own line.
if (condition)
break;
if (condition2)
return 0;
When breaking long lines with operators, the operator goes at the beginning of the new line.
if ((flags & FOO)
|| (flags & BAR))
Don't leave whitespace at the end of lines.
In function definitions outside classes the return type should go on its own line
static int
Magic::create_something_wonderful(int random)
{
return magic_happens(random);
}
Base classes can be put in the same line as the class name, if they fit. Otherwise, each base class must go on its own line.
So write either
class Foo : public Bar, public Baz
{
// stuff
};
or
class Foo
: public Bar,
public Baz
{
// stuff
};
Classes should be structured as follows:
class Foo
{
// enums and structs
public:
// Ctors
// public functions
protected:
// protected functions
private:
// private functions
// public, protected, and private variables at the end.
};
Opening and closing braces are placed on individual lines and they are indented by 2 spaces.
if (foo)
{
bar();
foo();
}
else
{
baz();
biz();
}
One exception to this rule is when a new function, class, namespace or enum is declared. Then the indentation is omitted.
The body of very short inline functions inside class definitions may go on a single line. Add spaces around the braces:
class Foo
{
int is_a_foo()
{ return true; }
};
The braces of an empty class body go on a new line below the class declaration
class Foo
{};
Use parenthesis only in one of the following cases:
- to improve readability in cases where knowledge of operator precedence is uncommon
- when expressions get complicated and consist of multiple sub-expressions and it is not possible to split up the expressions
- when a compiler warning demands it
Common knowledge of operator precedence includes:
-
==
and!=
are stronger than&&
and||
-
=
is less strong than common expressions
Examples in which no parenthesis are needed:
enum
{
Name = expression
};
int x = expression;
int a = x * y + z;
if (a == b && c == d) ...
if (a != b || c == d) ...
A space is inserted after most keywords. Exceptions to this rule are keywords
which are used like functions such as sizeof
and typeof
.
Use one space around the binary and ternary operators:
= + - < > * / % | & ^ <= >= == != ? :
Don't put space after the unary operators:
& * + - ~ !
Don't put space before the postfix increment and decrement unary operators:
++ --
Don't put space after the prefix increment and decrement unary operators:
++ --
We don't use camel-case. Names, types and constants start with a capital letter and concatenate multiple words using _.
namespace Util
{
class Br_manager
{
enum Some_enum
{
Item_1,
Item_2
};
};
}
Local variable names should be descriptive and short. Using one-letter names is considered bad as it hinders grep'ing.
Public members start with a lowercase letter. Private members start with an underscore.
class Rect
{
public:
l4_uint32_t x;
l4_uint32_t y;
private:
l4_uint32_t _area;
};
Usually you should use class
for class definitions. struct
may be only
used for classes when all members of a class are public.
while (condition)
{
multiple;
statements;
}
while (cond)
single_statement;
for (unsigned i = 0; i < 10; ++i)
{
multiple;
statements;
}
for (...)
single_statement;
do
{
code;
}
while (condition);
for (;;)
{
code...;
}
Put the const
keyword after the affected type. Put a space before a pointer
asterisk *
and attach it to the declared identifier or a following const
.
Motivation: Everything left of const
is constant:
FOO const *const
- The left-most
const
makesFOO
a constant object. - The right-most
const
makes the pointer constant.
Examples:
int i;
int const c = 42;
int *p1;
int *const p3 = &i; // const pointer to a (non-const) int
int const *p2; // pointer to a const int
int const *const p4 = &c; // const pointer to a const int
Use enum
types to define integral constants wherever possible. Avoid static const
and static constexpr
.
We use noexcept
instead of throw()
.
Resources should be managed according to the RAII paradigm. Avoid explicit malloc/free/new/delete in C++ code.
malloc
/free
must not be used in C++.
L4Re applications may use exceptions only to detect unexpected error conditions. These are errors that have one of two consequences:
- the application prints an error and exits
- in a server loop, processing of the current client request is aborted and an error is returned to the client.
Install the special L4 terminate handler from #include <terminate_handler-l4>
to ensure that exceptions print a meaningful message. You can also add a
catch-all try-catch in the main function. As a rule, this should be the only
try-catch you should need in your application.
Code must be written in a way that minimizes the need for casts. In particular, consider the value range and signedness for POD variables and be mindful of narrowing casts.
When having to cast, always use C++-style casts. The following rules apply:
- when using
dynamic_cast
the result must be checked fornullptr
-
reinterpret_cast
may only be used in dedicated allocator functions that provide typed memory
Do not trust the content of received IPC. Use the RPC framework to ensure correct handling of the IPC buffer and check the value range of parameters.
When a function returns an error, the caller must always check for the error
condition. You may use L4Re::chksys()
to convert the error to an exception.
Do use assertions to
- express pre- and postconditions of functions,
- check for programming errors that are not expected to ever occur.
Don't use assertions to
- check for errors that may be expected in production
- stop the program on an unexpected condition.
Assertion must not have side effects. The program must work safely when they are disabled. In particular, the core components must be able to handle unexpected conditions in a way that the system remains in a safe state.
Do not use goto
, setjmp
or longjmp
.
The only exception is in C code: goto
may be used to clean resources during
error handling.
In C files and C compatible header files we use /* ... */
, in C++ code we use
// ...
for comments.
If we modify 3rd party code e.g. a library or Linux we apply the coding style rules from the 3rd party project.
Use two spaces for indentation of conditionals:
ifeq ($(ARCH),mips)
SRC_CC_test_$(ARCH)_thread_vcpu_ext += mips_thread_vcpu_ext.cc
endif
- we use the
\command
Syntax - we use doxygen's Markdown instead of HTML or doxygen commands
- we have autobrief enabled, so don't use
\brief
- place an empty line after the brief description
- The detailed description is placed after the documentation of the return values. Put an emtpy line before the detailed description.
- parameter names must be left-aligned, as well as parameter descriptions
- at least two spaces between parameter name and description
- Parameter directions:
- the default is Input, just use plain
\param
-
\param[in,out]
is used if the parameter is used to pass arguments into the function and the function also returns values through this parameter -
\param[out]
is used if the called function stores or returns data through this parameter
- the default is Input, just use plain
- These rules apply to template parameters as well.
- place an empty line between parameter and return value documentation
- use
\retval
in case the function may return different return values and if possible describe each of them
/**
* A brief description of the function
*
* \tparam T Description of template parameter.
* \param par1 Description of parameter 1.
* \param[out] par2 Description of parameter 2. Caller must allocate memory.
* \param[in,out] par3 Description of parameter 3.
*
* \retval 0 The function returned successfully.
* \retval -L4_ENOMEM Not enough memory to create the new kernel object.
*
* \pre Add expected conditions here. Expectations for specific parameters
* may also be added in the respective param section.
*
* \post Describe conditions here that the caller can expect after
* execution. Don't forget to mention when they are only valid
* if execution was successful.
*
* Longer detailed description of the function. This may span multiple
* paragraphs. But the line length is limited to 80 characters.
*/
The L4Linux coding style mirrors the Linux coding style. However there are a few inaccuracies that need to be made clear:
When indenting parameters, you shall use tabs to reach the indent level of the function, and then use spaces to align the parameter to the first one. The reason is that if you used a mixture of tabs and spaces, the indenting of the function parameter breaks.