Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Coding Guidelines

Introduction

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

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.

Comments

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 Declarations

Pointer "*" and reference "&" designators shall have one space preceding them and no space following them. For Example:

int *example_variable;
void *example_function(const int &example_variable);

Line Endings

All files must use Unix style 'LF' line endings. Any other line ending is strictly prohibited including Windows 'CR+LF'.

Tabs

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.

Language keywords

A space must always precede text that follows a language keyword. For Example:

for (int i = 10; i > 0; i++)
{
if (i = k)
{
break;
}
}

Notice the space that follows the keywords "for" and "if".

Braces and Indentation

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:

namespace example_namepace
{
void example_function(int param)
{
if (param == 0)
{
// do something
}
else
{
do
{
--globalVariable;
} while (--param);
}
}
class Object
{
public:
Object()
{
}
bool return_true()
{
return true;
}
class Internal
{
public:
Internal()
{
}
};
};
} /* ExampleNamespace */

Line Size

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.

Include Order

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:

/* @file Example.cxx (this is an abbreviated file for example only)
*/
#include "Example.hxx"
#include <new>
#include <cstdio>
#include "nmranet/NMRAnetNode.hxx" /* needed for access to class Node */
/* This is a very long comment that would ordinarily exceed 80 characters
* of text so it is on its own line above the include to document the
* inclusion of nmranet/NMRAnetStream.hxx
*/
#include "nmranet/NMRAnetStream.hxx"
#include "os/OS.hxx"
#include "utils/Map.hxx"

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.

Naming Conventions

Styles

  1. UPPER_CASE, all characters upper case, each logical word separated by '_'
  2. lower_case, all characters lower case, each logical word separated by '_'
  3. PascalCase, first character upper case, each logical word capitalized
  4. camelCase, first character lower case, each logical word capitalized

Hungarian Notation

Hungarian notation (adding of descriptive prefix characters to names) is strictly forbidden and considered unnecessary clutter.

Filenames

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:

endian.h // standard C header file that could also be used in C++
hw_init.c // standard C source file
Node.hxx // C++ class Node implementation
Node.cxx // C++ class Node implementation
build_cdi.py // Executable Python script with permissions -rwxrwxr-x
info.dox // Doxygen comment only file
Node information.
Definition Devtab.hxx:549

Include Guards

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:

#ifndef _UTILS_EXAMPLEFILENAME_HXX_
#define _UTILS_EXAMPLEFILENAME_HXX_
#include <new>
/* Body of header file */
#endif /* _ExampleFilename_hxx_ */

Constants

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:

const int EXAMPLE_CONSTANT = 1000;

Functions

Functions use lower_case. For example:

void example_function(void)

Exceptions include C++ constructors and destructors. These will use PascalCase.

Global and Member Variables

Global and Member variables use camelCase. For example:

int exampleGlobalVariable;
struct example_struct
{
int exampleMemberVariable;
};
class Object
{
int exampleMemberVariable;
}

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.

// This example is okay
class Object
{
int exampleMemberVariable_;
};
// This example is NOT okay
class Object
{
int _exampleMemberVariable;
}

Local Variables and Function Parameters

Local variables and function parameters use lower_case. For example:

void example_function(int function_parameter)
{
int local_variable;
}

Macros

Macros use UPPER_CASE. For example:

#define EXAMPLE_MACRO
#define EXAMPLE_FUNCTION_MACRO(_var1, _var2)

Structures and Enumerations

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:

typedef struct example_struct
{
int memberVariable;
} ExampleStruct;
typedef struct
{
int memberVariable;
} ExampleStruct;
struct example_struct
{
int memberVariable;
};
typedef enum example_enum
{
ENUM_VALUE = 5;
} ExampleEnum;
typedef enum
{
ENUM_VALUE = 5;
} ExampleEnum;
enum example_enum
{
ENUM_VALUE = 5;
};

The structure tag uses lower_case and the structure identifier uses PascalCase.

C++ Structures, Classes, and Enumerations

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:

class ExampleClass
{
public:
ExampleClass();
protected:
private:
int memberVariable;
};
struct ExampleStruct
{
int memberVariable;
};
enum EnumExample
{
ENUM_VALUE = 5;
};

C++ Namespaces

In C++, namespaces use lower_case.

For example:

namespace example_namespace
{
}

Handles

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:

typedef struct semaphore
{
int count;
int owner;
} Semaphore;
typedef void *sem_t;
sem_t sem_create(void)
{
Semaphore *sem = malloc(sizeof(Semaphore));
sem->count = 0;
sem->owner = 0;
return (sem_t)sem;
}
OSSem sem[1]
One semaphore required per instance pointer.
Definition CC32xxSPI.cxx:55

Loop Counters

It is a best practice to use the variable names i, j, and k for arbitrary loop counters. For example:

for (int i = 0; i < 10; ++i)
{
for (int j = 0; j < 10; ++j)
{
for (int k = 0; k < 10; ++k)
{
}
}
}

Switch Statements

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:

switch (variable)
{
default:
break;
case 1:
/* fall through */
case 2:
i = j + variable;
break;
}

If/Else Statements

OpenMRN source always uses hanging braces. Additionally, single line if statements must use braces. For example:

if (i > 0)
{
j = i;
}
else
{
k = i;
}

Notice that the brace is always on its own line. The following two examples are both unacceptable:

if (i > 0)
j = i;
else
k = i;
if (i > 0) {
j = i;
} else {
k = i;
}

Coding Standards

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.

Reference Arguments

All arguments passed by reference shall be const.

void example_function(const int &example_param);

C++ Exceptions

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.

Run Time Type Information (RTTI)

Use of Run Time Type Information (RTTI) is prohibited.

C++ Streams

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.

printf("string\n"); // allowed
cout << "string"; // not allowed

Preincrement and Predecrement

In use cases where ++i form can be used instead of i++, use the ++i form. The ++i form produces fewer instructions.

8, 16, 32, and 64 Bit Compatibility

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.

Preprocessor Macros

The use of preprocessor macros is not forbidden. However, inline functions, enums, and const variables are preferred.

0 and nullptr/NULL

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.

int i = 0;
float j = 0.0;
char *p = NULL;
char *p = nullptr;
char c = '\0';

auto

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

Brace initialization may be used, unrestricted.

int array[] = {0, 1, 2, 16, 7};

Boost

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.

C++11 (formerly C++0x)

Use of C++11 language extensions is allowed.

Exceptions

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.