|
Open Model Railroad Network (OpenMRN)
|
OpenMRN source code adheres to strict coding guidelines. These guidelines apply to all C and C++ source code and all text files where applicable. The coding guidelines are broken down into two parts: Coding Style and Coding Standards.
Coding Style deals with aspects such as naming conventions, whitespace, and brace placement. Observance of consistent whitespace rules is important for all text files to be readable in the largest possible number of editors and diff tools.
Doxygen comments are required for all language members. A doxygen warnings file "doc/warnings" is generated by doxygen and can be used to verify compliance. The quality of the Doxygen output is only as good as the comments that are provided as inputs. Developers are expected to provide as much verbosity in their commenting as reasonably possible and include Doxygen cross references whenever applicable. The Javadoc style of comments is preferred. The use of "!" for the Doxygen tag is forbidden strictly for consistency reasons. The "@" character for Doxygen tags is preferred over the "\" character for Doxygen tags. Use of the "" character is allowed.
/// Valid single line Doxygen comment /** Valid Single line Doxygen comment */ /** Valid Single line Doxygen comment */ //! Invalid single line Doxygen comment /*! Invalid single line Doxygen comment */ /// Valid multi-line Doxygen /// comment /** Valid mulit-line Doxygen * comment */ /** Invalid mulit-line Doxygen comment */ /// This is a valid Doxygen comment for int i int i; /** This is a valid Doxygen comment for int i */ int i; int i; ///< This is a valid Doxygen comment for int i int i; /**< This is a valid Doxygen comment for int i */ /** @param - preferred tag style */ /** \param - allowed tag style, not preferred */
Multi-line comments shall use C style comments blocks with intermediate "*" characters starting each line and aligned appropriately as shown in the below example. Multi-line comments using the C++ "//" style are also acceptable. Single line comments may use C or C++ style.
// This is a valid comment for int i int i; /* This is a valid comment for int i */ int i; /* This is a valid multi-line comment * for int i */ int i; // This is a valid multi-line // comment for int i int i; /* This is an invalid multi-line comment for int i */
For multi-line C style comment blocks contained within a *.dox documentation file, an exception to the rules is made. In the case of a *.dox file, the intermediate "*" character starting each line is optional, and not required.
Pointer "*" and reference "&" designators shall have one space preceding them and no space following them. For Example:
All files must use Unix style 'LF' line endings. Any other line ending is strictly prohibited including Windows 'CR+LF'.
Tabs are prohibited in all files. The standard delimiter is 4 spaces. Most text editors can be configured to automatically insert spaces in place of tabs. This is the recommended setup for editing OpenMRN files. For GNU Makefiles there is an exception. Tabs may be used within GNU Makefiles only where the syntax requires it.
A space must always precede text that follows a language keyword. For Example:
Notice the space that follows the keywords "for" and "if".
The brace and indentation style most closely matches that commonly known as Allman. Though there are many possible styles to choose from, Allman is often sited as most commonly used and one of the easiest for humans to read and parse. The OpenMRN style does not intend to be an exact match for any given interpretation of the Allman style.
Allman style places the brace "hanging" on its own line and in alignment with the preceding statement above it. Statements within the brace are indented by four spaces. Inline class member functions are also required to have braces on their own separate line, no shortcuts.
The "pubic", "protected", and "private" keywords are not indended, rather they are in line with the opening brace of a class or structure.
The contents of a namespace are not indented.
Some examples:
The line size of text should typically be kept to 80 characters or less. This is not a requirement and left up to the discretion of the developer. The developer is encouraged to keep their line sizes to 80 characters or less, however exceeding 80 characters by a small amount may sometimes be preferred for reasons of readability. The 80 character limit that is a common style guideline requirement is to a large extent a legacy of standard 80 character wide terminals. Modern editors are typically much more flexible on viewing width. Most modern editors can provide an 80 character water mark and developers are encouraged to enable this in order to be more aware of their line sizes so that they can act appropriately.
Typically (though not strictly required) every source file (*.c or *.cxx) will have a corresponding header file (*.h or *.hxx) with the same name. Within a give source file, its corresponding header will always be included first using quotes. Next any system/library headers will follow using angle brackets in unspecified order. All other includes will follow in lexicographical (alphabetical) order using quotes. There will be a blank line between the corresponding header file and the start of the system/library headers and another blank line between the end of the system/library headers and the start of all the rest of the headers. Comments may optionally follow an include declaration but should only be on their own line(s) above the include if in the rare case the comment would otherwise cause the line to exceed 80 characters. The comment in this case is not to be a Doxygen comment. Example:
It is important to note that there is quite a bit of legacy code that was written prior to the Include Order style guideline. This code is considered incorrect and will be updated over time to be in compliance.
Hungarian notation (adding of descriptive prefix characters to names) is strictly forbidden and considered unnecessary clutter.
C language source and header files will use lower_case. Source files will have an extension of ".c" and header files will have an extension of ".h". A C file shall be named after the major construct defined within it.
C++ language source and header files will use PascalCase. Source files will have an extension of ".cxx" and header files will have an extension of ".hxx". A C++ file shall be named after the major class or object within it.
Mixed language (C and C++) header files will use lower_case and have an extension of ".h".
In general, a given source/header file should limit itself to one major construct. This is not a hard and fast rule and somewhat up to the discretion of the implementor.
Unix file permissions for C and C++ source files will have Unix file permissions of -rw-rw-r–, or u+rw, g+rw, o+r.
Scripts, typically placed in the bin/ directory, will have appropriate extentions based on convention, for example ".py" for Python, ".perl" for Perl, etc... Scripts will have executable Unix file permission bits set.
Doxygen files, files that contain only doxygen comments, shall have the extension ".dox".
If developing on a system that does not support Unix file permissions, such as Windows, care must be taken not to pollute the source tree with incorrect file permissions. Tools such as Cygwin and MinGW can aid in preserving Unix file permissions within Windows.
Some Examples:
Header files often implement include guards to protect from multiple inclusion of a header within a given source context. The style used for the tag adds a leading underscore "_", replaces any "." character with an underscore "_" character, and adds a lagging underscore "_". The case of the tag shall be all caps in the UPPER_CASE style. The Include guard should also contain the root directories of the file location. Additional #includes shall always fall within the body of the include guard. For a given filename of "ExampleFilename.hxx" in the utils directory, see the following example:
Constants, used in any context, supersede the style of the context and always use UPPER_CASE. This includes members of structures, classes, and enumerations. For example:
Functions use lower_case. For example:
Exceptions include C++ constructors and destructors. These will use PascalCase.
Global and Member variables use camelCase. For example:
It is important to note that member variables shall not have a leading underscore "_". It is however acceptable for classes and structures to add a underscore "_" suffix to their member variables. This is an optional extension of the naming conventions and may be used at the developer's discretion to avoid naming conflicts with local and parameter variabls, but please be consistent within a given class or structure.
Local variables and function parameters use lower_case. For example:
Macros use UPPER_CASE. For example:
Structures make use of both lower_case, and PascalCase. Note that these examples are for C code. C++ has its own style defined in C++ Structures, Classes, and Enumerations. For example:
The structure tag uses lower_case and the structure identifier uses PascalCase.
In C++, classes, structures, and enumerations use PascalCase.
In general, public members should be declared first, followed by protected members, and finally private members. It may occasionally be desirable to diverge from this convention, and as such, this is not a hard and fast rule.
For example:
In C++, namespaces use lower_case.
For example:
Within C code, it is common to define a handle. A handle is typically a typedef to a void * pointer that a library passes to an application to keep track of an object. Because the object is typecast to a void * pointer, the application does not have easy access to data that a library wants to keep private. Handles use lower_case suffixed by an "_t". For example:
It is a best practice to use the variable names i, j, and k for arbitrary loop counters. For example:
Within switch statements, case values that do not have their own break statement shall provide a "fall through" comment. All switch statements must define a default case. The case body is indented from its opening and closing braces. For example:
OpenMRN source always uses hanging braces. Additionally, single line if statements must use braces. For example:
Notice that the brace is always on its own line. The following two examples are both unacceptable:
Coding Standards are the aspects that deal with impacts on the lanquage implementation such as what attributes of the C/C++ language are off limits or restricted in use.
All arguments passed by reference shall be const.
The throwing and catching of C++ exceptions is not supported within OpenMRN. Most of the default build settings disable exception handling in the compiler and linker.
Use of Run Time Type Information (RTTI) is prohibited.
The use of C++ streams is forbidden. Use of <cstdio> in C++ or <stdio.h> in C is allowed, so long as its use can be disabled at compile time to save code space.
In use cases where ++i form can be used instead of i++, use the ++i form. The ++i form produces fewer instructions.
Code shall be written to be compatible with targets having native bit sizes including 32 and 64 bits. OpenMRN is not guaranteed to be compatible with 8 and 16 bit targets, however, reasonable efforts shall be made to try and maintain compatibility with these smaller targets. Note that in the C standard, the "int" type is not guaranteed to be greater than 16 bits, and on many 8 and 16 bit targets, an "int" is indeed 16 bits.
The use of preprocessor macros is not forbidden. However, inline functions, enums, and const variables are preferred.
Use 0 for integers, 0.0 for reals, nullptr or NULL for pointers, and '\0' for chars. In C++ source code, nullptr is prefered over NULL, and NULL is considered deprecated.
Use of auto is only allowed for local variable declaration. In general, the use of auto is not preferred, but it is allowed for long cluttered type names or when required by the C++ language semantics.
Brace initialization may be used, unrestricted.
At the moment, there are no approved Boost libraries for use in OpenMRN. In the future, select Boost libraries may be approved on a case by case basis provided there is sufficient justification.
Use of C++11 language extensions is allowed.
Third party source code does not follow these coding guidelines. Instead, follow the guidelines already established by the third party. Examples of third party source code include the XML and JSON parsers. It is also acceptable to diverge from any of the these coding guidelines provided a verbose justification is provided with comments inline as to why the divergence is necessary.