-
Notifications
You must be signed in to change notification settings - Fork 0
Coding guidelines
This section contains the general coding guidelines for Wiselib development. The following lines illustrate the basic idea of our coding style by a simple example class template, but the next sections also describe our style in more detail.
// template parameters become an "underscore-P"-suffix. // class template parameters are written mixed upper/lower case. template <typename TemplateParameterA_P, typename TemplateParameterB_P, typename TemplateParameterC_P> class ClassNameMixedUpperLower { public: // template parameters are then typedefed without the "underscore-P"-suffix typedef TemplateParameterA_P TemplateParameterA; typedef TemplateParameterB_P TemplateParameterB; typedef TemplateParameterC_P TemplateParameterC; typedef typename TemplateParameterA::TemplateParameterAA TemplateParameterAA; typedef typename TemplateParameterA::TemplateParameterAB TemplateParameterAB; // provided data types are written lower case with underscores typedef uint8_t block_data_t; // methods only lower case with underscores int methods_are_stl_like( void ) throw(); // access to stored stuff is provided via two methods, const // and writable. // latter is indicated by _w suffix // get_ is omitted (so usual pair is set_value(value), // value(void) const SomeThing& some_thing( void ) const; SomeThing& some_thing_w( void ); void set_some_thing( SomeThing& ); private: int member_variables_have_an_underscore_; };
This section describes the Look & Feel of our source code. It contains the intending, the placement of brackets, when and where to set single spaces, and the maximal line width.
Indenting can be done either by spaces or by tabs, but consistently per file. So do not mix spaces and tabs in an existing file. Instead, use what the original author has used. If you use spaces, indent with exactly three spaces.
Brackets are consistently written in separate lines, and thus never written behind a statement. This is done for conditional expressions, loops, classes, and namespaces.
if ( a == b ) { // do something } else { // do something else } for ( int i = 0; i < 5; i++ ) { if ( foo == i ) { // do something } } class MyClass { [...] };
When writing one-liners like
for ( int i = 0; i < 5; i++ ) j++;
brackets are not mandatory (albeit they can be used), but if there are nested one-liners, one should use brackets for a better readability. So better write
if ( a == b ) { for ( int i = 0; i < 5; i++ ) { j++; } }
instead of leaving the brackets out.
To enhance readability, spaces are used to separate declarations (after a comma), mathematical expressions, and before or after parenthesis.
For example, a function header looks like this:
void foo( int a, int b ); void bar( void );
An example for a conditional expression may be:
if ( a == b )
And a mathematical expression should be written as follows:
c = d + 4;
Line wrapping is done after at most 80 characters to enhance readability and portability. Note that there are also users who work in text-based environments.
If a mathematical expression must be wrapped, split it regarding to the context of the expression:
if (( a == b ) && ( c > d || e < f || d > e ) && ( g != h ))
Separators are used to enhance readability. They can be used either inside of methods to group correlated statements, or outside of methods to distinguish one method against the other.
Group correlated statements together, and separate these groups by newlines. This enhances readability because it enables reading block by block in logical sections. Moreover, the general view is improved by doing so. Have a look at the following examples.
if ( from == Radio::id(os()) ) return false; uint8_t msg_id = *data; if ( msg_id == DsdvBroadcastMsgId ) { BroadcastMessage *message = (BroadcastMessage *)data; routing_table_[from] = RoutingTableEntry( from, 1 ); update_routing_table( from, *message ); } return true;
Another example:
BroadcastMessage message; message.set_msg_id( DsdvBroadcastMsgId ); message.set_entry_cnt( routing_table_.size() ); int idx = 0; for ( RoutingTableIterator it = routing_table_.begin(); it != routing_table_.end(); ++it ) { message.set_entry( idx++, *it ); }
To separate one method implementation against the other, there is a delimiter between any two methods as shown in the following source.
[...] // ---------------------------------------------------------------------- template<TemplateParameterA_P, TemplateParameterB_P> void MyClass:: my_function( void ) { [...] } // ---------------------------------------------------------------------- template<TemplateParameterA_P, TemplateParameterB_P> MyClass:: my_other_function( void ) const { [...] } // ---------------------------------------------------------------------- [...]
The following options of astyle might help you to reformat sources according to some of the guidelines above.
Put brackets on single lines:
--brackets=break
Put spaces around operators:
--pad=oper
Insert spaces at the insides of parenthesis:
--pad=paren-in
Indent with with 3 spaces per indentation level
--indent=spaces=3
The following discussion on the mailing list might also prove to be useful http://www.ibr.cs.tu-bs.de/pipermail/wiselib/2012-April/000060.html
This section contains rules for naming identifiers like variables and methods, and filenames. There are rules for both selecting names and spelling names.
When you think about names for identifiers, choose meaningful ones. This enhances readability, and suppresses the need for additional comments.
Hence, do not write the following:
int t; // time of last reception
Instead, write it as follows:
int time_of_last_reception;
Then, use only comprehensible abbreviations for names. It is valuable to use abbreviations like idx for index, or cnt for count. But do not abbreviate a sentence like time of last reception to tolr.
In general, http://www.objectmentor.com/resources/articles/naming.htm is a mentionable article that describes naming in detail and is worth reading.
Methods and parameters are completely written in lower case characters ('a'..'z', '0'..'9', '_'). They begin with a letter ('a'..'z'). Multiple words are separated with underscores. Examples are:
void set_node_label( int ); void do_something( void ); int time_of_last_reception; int node_cnt;
Classes are written in mixed upper and lower case characters ('A'..'Z', 'a'..'z', '0'..'9'). They begin with an upper case letter ('A'..'Z'), followed by lower case ones. Multiple words are separated by a new upper case character. If a word is a known abbreviation (like 'MAC Layer', for example), only the first character is written as an upper case one. Examples are:
class MacLayer; class DsdvRouting; class ContikiRadio;
If class members can be accessed (per set or get) via methods, these methods follow a certain naming rule. All setters have the prefix set_* :
void set_some_thing( SomeThing& obj ) throw();
Getters do not have any prefix. To distinguish writable and constant getters, writable ones get the suffix *_w :
const SomeThing& some_thing( void ) const throw(); SomeThing& some_thing_w( void ) throw();
Private members of classes always end with an underscore:
int private_member_of_a_class_;
As a side effect, variable names in setters and constructors can easily be chosen.
void set_value( int value ) { value_ = value; }
Sourcefiles are named after the particular class (e.g., the file dsr_routing.h contains the class DsrRouting). Like naming methods and parameters, filenames are completely written in lower case characters ('a'..'z', '0'..'9', '_'). They begin with a letter ('a'..'z'). Multiple words are separated with underscores.
File extensions are *.h for headers, and *.cpp for source files (of course, source files should be rare in a template library).
When creating directories, there are the same rules as for files. Use only lower case characters or digits, separate by using the underscore, and do not use spaces.
With the generic part of Wiselib, it is not allowed to use virtual inheritance. Besides the obvious performance drawback (one additional pointer per virtual class; one additional level of indirection when calling virtual member functions), it is much more difficult for the compiler to optimize code around virtual member functions. E.g., even one-liners can not be inlined when declared as virtual
.
There is no necessity to use virtual inheritance. The only advantageous usage would be for callback registration. That is, a class that would like to register a callback function, inherits from a common interface and implements the callback function. However, this behavior can easily (and much more efficiently) be substituted by the Wiselib callback concept using Delegates.
To allow compatibility with all platforms, exceptions are never used within the Wiselib.
To allow compatibility with all platforms, dynamic memory allocation (new
/delete
/malloc
/free
) is never used within the generic part of the Wiselib.
In contrast, it is allowed to use dynamic memory allocation in the external interface, because it consists of platform specific code. And thus, it is known whether dynamic memory allocation can be used or not.
Similar to the dynamic memory allocation, it is not allowed to use C++ header files within the generic part of the Wiselib due to compatibility issues. There are compilers, for example the msp430-g++
, which are C compilers only with added C++ support. Hence, headers such as cmath
or cstdlib
are '''not''' allowed to be used. Instead, use the corresponding C header files math.h
or stdlib.h
.
The usage of #define
should be avoided, because there are enough alternatives available in C++ (const (const int foo = n
), inline functions, templates), and macros also do not obey scopes or type rules. So do not use macros unless it is absolutely inevitable. Especially, avoid using macros in header files. If at all, use them in source files where they are only applied strictly local.
Make yourself familiar with the programming language C++, and use available constructs instead of reinventing the wheel. Especially, make use of the pSTL, their containers and iterators. If something is worth made generic, use templates.
If a method of a class does not modify the state of an object, declare it as const
. As const objects are often used in the Wiselib, this increases usability for developers.
All comments and all documentation are written in English, because the Wiselib is used in an international environment.
Header files are completely documented in doxygen-style to be able to automatically generate documentation in HTML, pdf, or the like. See http://www.stack.nl/~dimitri/doxygen/manual.html, for example, for details. Thereby the class itself, each method, and each parameter must be described.
The description of the class starts with a brief description of what the class is supposed to do. This is an one-liner that is used together with the doxygen keyword brief . Then, a detailed description follows. It contains the general behavior of the class, the context in which it is used, potential usage hints (e.g., with examples), and so on.
Next, methods must be described. If suitable, they are grouped with the aid of the doxygen keywords @name , @param , and @returns. Then, for each function the purpose and usage are documented. Optionally, the description can be started with an one-liner and the keyword brief . At last, all parameters (if there are any) and the return value (if not void) are documented.
Then, the class members are documented, generally with a short one-liner that describes the purpose of the value and in which context it is used.
However, here is a skeleton of a general header file:
/** \brief Short Description (one line) * * Detailed description (multiple lines). */ template <typename TemplateParameterA_P, typename TemplateParameterB_P, typename TemplateParameterC_P> class DoxygenExample { public: ///@name Constructor/Destructor /// /** constructor description, optionally with \brief * */ DoxygenExample(); /// ///@name Getters and Setters /// /** function description, optionally with \brief * * \param value brief description */ void set_value( int value ); /** function description, optionally with \brief * * \return brief description */ int value( void ); /// private: /// purpose of value int value_; };
In general, give rough descriptions of what you are doing and why. Do not comment obvious lines like
a += 1; // add one to a
Then, use //
for documentation, also for multi-line comments
(note that this rule holds only for in-source documentation):
// first line of comment ... // ... followed by the second line.