C/C++ Coding Style & Standards

Coding practices for embedded development

 

Table-of-Contents

 1      Synopsis.. PAGEREF _Toc532214100 \h 2

1.1        Disclaimer.. PAGEREF _Toc532214101 \h 2

2      Standards.. PAGEREF _Toc532214102 \h 3

2.1        No dynamic memory allocation.. PAGEREF _Toc532214103 \h 3

2.2        Minimize the public interface of every class. PAGEREF _Toc532214104 \h 3

2.3        Use const wherever possible. PAGEREF _Toc532214105 \h 3

2.4        No type casting. PAGEREF _Toc532214106 \h 3

2.5        No compiler warnings. PAGEREF _Toc532214107 \h 3

2.6        No explicit constants. PAGEREF _Toc532214108 \h 4

2.7        Do not use the NULL macro.. PAGEREF _Toc532214109 \h 4

2.8        Do not use preprocessor macros. PAGEREF _Toc532214110 \h 4

2.9        Conditionally compiled code is not allowed.. PAGEREF _Toc532214111 \h 4

2.10       Virtual Destructors. PAGEREF _Toc532214112 \h 4

2.11       Copy constructor and assignment operator.. PAGEREF _Toc532214113 \h 4

2.12       Pass/Return objects by reference. PAGEREF _Toc532214114 \h 4

2.13       Protect header file with #ifndef read-once latch. PAGEREF _Toc532214115 \h 5

2.14       Minimize the #include statements in your header files. PAGEREF _Toc532214116 \h 5

2.15       Avoid the global namespace unless absolutely necessary. PAGEREF _Toc532214117 \h 5

3      Style Guide. PAGEREF _Toc532214118 \h 6

3.1        Comments. PAGEREF _Toc532214119 \h 6

3.1.1     Header Files. PAGEREF _Toc532214120 \h 6

3.1.2     CPP Files. PAGEREF _Toc532214121 \h 6

3.1.3     Use doc++ comments. PAGEREF _Toc532214122 \h 6

3.2        Naming.. PAGEREF _Toc532214123 \h 7

3.2.1     Classes, Methods, Variables. PAGEREF _Toc532214124 \h 7

3.2.2     Files and Directories. PAGEREF _Toc532214125 \h 7

3.3        Tabs. PAGEREF _Toc532214126 \h 7

3.4        Flow Control Statements. PAGEREF _Toc532214127 \h 8

 

 

 

1         Synopsis

This document defines the C++ coding standards and style for developing embedded software.  Code standards and code style are often mixed together, which is unfortunate. Standards should be reserved for coding practices which bear directly on the reliability of code. Violating a precept of a code standard puts your code in jeopardy of error. Style is another matter. It deals with issues like formatting and naming conventions. More than anything style affects the readability of your code. And it is mainly a matter of taste.

 

The coding standards presented here are specifically tailored to working in an embedded environment – were the robustness and reliability of the software is critical.  It is unacceptable for embedded software to periodically reboot because of exhausted/squander resources (such as no memory available).

 

The establishment of a common style will facilitate understanding and maintaining code developed by more than one programmer as well as making it easier for several people to cooperate in the development of the same program. Using a consistent coding style throughout a particular module, package, or project is important because it allows people other than the author to easily understand and (hopefully) maintain the code.  Most programming styles are somewhat arbitrary, and this one is no exception.

1.1           Disclaimer

This document is a selective collection of coding style and standards excerpts from numerous white papers and publications.  Discussion of style and standards is a long honored tradition in software development.  This document is attempt by the author to define a minimum set of style and standards that are helpful for embedded development, but at the same time practical to implement. The idea here is to maximize the “bang for the buck”.  Coding style and standards require discipline on the part of the developers to be followed.  The more “painful” an organization’s style and standards are, the less likely that the developers will faithfully follow the procedures.  Process and its sibling documentation are some of the first casualties of schedule pressures.  The moral of the story is to keep your style and standards to a manageable set – especially in time of crisis.

 

 

2         Standards

The standards documented here are targeted at reducing errors while developing code.  This includes both compile time and run time errors.  For the standards to be effective, strict enforcement is required.  The following standards should not be violated.  Every time a rule is broken, it must be clearly documented in the code.  This documentation serves two purposes. First it provides crucial information to other developers when maintaining the code. Second, it forces the original programmer to think about/fully justify why the standards do not apply.

2.1           No dynamic memory allocation

To prevent memory leaks and fragmentation – no dynamic memory allocation is allowed.  The application may allocate memory (from the heap) at start-up – but not once the system is “up and running.”  This practice guarantees the system will not fail over time due to lack of memory. 

 

For objects that must be dynamically created/deleted – the application programmer is require to pre-allocate a memory pool (on a per object type basis) that will be used to construct object at run-time.  This requires the developer to use the “placement new” operator to create an object.  To delete an object, the developer must explicitly call the object’s destructor and then return the memory back its associated memory pool. 

Note: It goes without saying that malloc(), realloc()) and free() shall not ever be used.

2.2           Minimize the public interface of every class.

Do not make data members public.  Keep member functions private or protected unless they are truly intended to be part of the public interface.

2.3           Use const wherever possible.

constness is next to Godliness. Every possible modification of data should occur according to explicit relaxation of default read-only policy. Think const. Member functions should be const by default. Only when you have a clear, explicit reason should you omit the const modifier on member functions.

2.4           No type casting.

Take every possible measure to avoid type casting.  Errors caused by casts are among the most pernicious, particularly because they are so hard to recognize. Strict type checking is your friend – take full advantage of it!

2.5           No compiler warnings.

Code that compiles with warnings will not be accepted for integration.  Also while developing code eliminate the warnings as-soon-as-possible, as the warnings tend to be the source of logic and run-time errors.  NOTE: You should always compile with the compiler switch that treats warnings as errors.

2.6           No explicit constants

Do no write explicit constants into code, except to establish identifiers to represent them. The exception to the rule is the constant “0”.  Always use the C++ keywords true and false for Boolean values.

2.7           Do not use the NULL macro

Always use 0, never NULL, in C++.  The “Annotated C++ Reference Manual”, by Ellis and Stroustrup [Addision-Wesley, 1990] does not guarantee that the defined constant NULL is compatible with all pointers types.  Some implementations define NULL as (void*)0, which cannot be assigned to another pointer type without a cast.

2.8           Do not use preprocessor macros

The Preprocessor knows nothing about C++.  Don't use preprocessor macros to do what C++ can do.  Do not use #define to set the value of a constant or create an “inline function”.  Use enumerations or const variables, and/or inline functions instead of preprocessor macros. 

2.9           Conditionally compiled code is not allowed

Do not use the preprocessor #ifdef/#ifndef constructs to support platform/hardware/system dependent code.  All platform/hardware/system dependent code must be isolated into individual files that are specific to the dependencies.  This means the developer – at the design stage – must identify and plan for handling these dependencies!

2.10     Virtual Destructors

If a class has a virtual function, declare the destructor virtual, too.

2.11     Copy constructor and assignment operator

Always declare a copy constructor and assignment operator for a class that has responsibility for dynamic storage.  The default copy operation provided natively by the compiler only provides a ‘bit-wise’ copy.  This default operation does not make a copy of what ever is dynamically stored in the class. By explicitly implementing the copy and assignment methods the developer is able to provide a “deep” copy of the class.  The copy/assignment operations are responsible for correctly copying the dynamic elements of the class.  NOTE: Strict adherence to the “No dynamic memory allocation” rule makes this rule moot.

2.12     Pass/Return objects by reference

References provide stricter semantics than raw pointers (i.e. a reference requires that the object it “points to” exists – where a raw pointer does not).  If there interface semantics are such that the object must always exist – use references in the interface even if the internal implementation is using pointers.  By using references in interfaces whenever possible, the compiler can enforce more of the semantics of the interface.

2.13     Protect header file with #ifndef read-once latch.

Use the following template for the read-once latch:

                #ifndef _namespace_classname_h_

      #define _namespace_classname_h_

     …

     #endif

For example

            #ifndef _jcl_InputStreamApi_h_

      #define _jcl_InputStreamApi_h_

      …

      #endif

 

2.14     Minimize the #include statements in your header files.

Headers files should only include those (header) files that are required for header file to compile.  Do not include files that are only used by the associated .cpp file. The #include statements in your header files define the dependency of the file –fewer the dependencies the better.

2.15     Avoid the global namespace unless absolutely necessary.

It should be obvious why you want to avoid polluting the global namespace.  If you ever “import” code from a developer, department, third party vendor, etc. there is potential for collision in the global namespace.  If a collision occurs – someone’s code must change.  Modifying “proven” code is bad thing!  The two common options to avoiding polluting the global namespace are: 1) Nest enumerations, constants, helper classes, etc. inside of existing classes.  2) Use the C++ namespace feature. 

 

 

3         Style Guide

The intent of the style guide is to establish a “style baseline” that all developers are required to follow.  This baseline provides a consistence across the source code files that aids in reading/maintain the code. In addition, following a common programming style will enable the construction of tools that incorporate knowledge of these standards to help in the development and maintenance of the code.

 

This guide specifies only basic requirements – the individual programmer is allowed to impose his/her own style/preferences on top of these requirements. There are only two absolute rules to observe when it comes to “creating your own” coding style. The first rule of coding style is consistency:

Establish a style and stick to it.

 

The second rule of style is tolerance:

When you must modify code written by others, conforming to a different style, adopt that style, do not convert the code to your style.

3.1           Comments

3.1.1   Header Files

Rule:     Header files must be completely documented.  This means every class, method, and data member must have comments.  Header files describe the interfaces of the system, and as such, should contain all the information a developer needs to use/understand the interface.

3.1.2   CPP Files

Rule:     The quantity/quality of the comments in a CPP file is left to the individual developers to decide.  If the corresponding header file is completely documented, comments in the CPP file add little value.

Rec:     Add comments whenever you feel that the code is complex, non-standard, and/or clever.  Remember if you want the luxury of other people maintaining your code – they must be able to understand it!

3.1.3   Use doc++ Comments in Header Files

Rec:     Document your code so that an external tool can extract the information stored in your header file comments.  For example doc++ and doxygen are freely available tools that are able to generate online (HTML) and offline (Postscript, man pages, etc) documentation from the raw source code. Detailed information on doc++ and doxygen can be found at: http://www.zib.de/Visual/software/doc++/, http://www.doxygen.org.  The following are examples of doc++ comment styles.

/** This abstract class …

 */

class FooBar {

public:

      /// Enables the Widget to begin sending data

      virtual void enableWidget() =0;

};

3.2           Naming

3.2.1   Classes, Methods, Variables

Rule:     All class names start with an upper case letter.

Rule:     All function names start with a lower case letter.

Rule:     All data members are prefixed with an ‘_’.  For example:

            int _myCount;

Rec:     Use mixed case to separate words in names – not an “_’.  For example:

            int getReferenceCount();   // OK

      int get_reference_count(); // NOT OK

Rec:     Do not abbreviate names/words; long names are preferred over cryptic abbreviations.

3.2.2   Files and Directories

Rule:     Almost all OS/Platform support ‘long’ filenames – use them.  Do not ‘encrypt’ you file and directory names into an arbitrary 8.3 format.

Rule:     Do not use mixed case in file and directory names.  Not all file system (and revision control systems) are case sensitive with respect to names.

Rec:     Avoid using dashes (-), underbars (_) and dots (.) in directory names.

Rec:     Directories are your friend.  Do not hesitate to create directories even if they only contain other directories or a single file.  Directories are a great tool for organizing and “naming” dependencies between source code files and packages.

3.3           Tabs

Rule:     Tabs stops will be set to 4!  The actual use of spaces vs. tabs when indenting is left up to the individual.

Rule:     When modifying someone else’s code – follow the original author’s tab style – whether or not it agrees with your own personal preference.

Sidebar:  It is amazing at how “religious” programmers can get about the use of tabs vs. spaces.  I have met more than one programmer who would rather be fired than change his tab style.  The subject of tab style ranks up there with the topics of curly braces and text editors for “religious fervor.”

3.4           Flow Control Statements

Rec:     The flow control primitives if, else, while, for and do should be followed by a block, even if it is an empty block For example:

while( /* do something */ )   // Bad

   ; 

 

while( /* do something */ )   // Good

   {

   }

 

Rec:     The block following a flow control primitive should always be bounded by brackets even it the block contains only one statement. For example:

if( isOpened() )        // Bad

   foobar();

 

if( isOpened() )        // Good

   {

   foobar();

   }