Coding Standard

Home Intro APIs Source Platforms Licensing FAQ News Acknowledgements


Up

SourceForge Logo

Coding Standard and Style guide for XMK

Contents

1      Synopsis

1.1        Disclaimer

2      Standards

2.1        No dynamic memory allocation

2.1.1     Allocating static memory

2.2        Minimize the public interfaces

2.3        Use const wherever possible

2.4        No compiler warnings

2.5        No explicit constants

2.6        Conditionally compiled code is not allowed for platform derivatives

2.7        Protect header file with #ifndef read-once latch

2.8        All header files are compatible with C++

2.9        Minimize the #include statements in your header files

3      Style Guide

3.1        Comments

3.1.1     Use Doxgyen comments in Header Files

3.1.2     No C++ comments

3.1.3     Fully documented public header files

3.1.4     Private header files and C files

3.2        Naming for the APL Package

3.2.1     Interface definitions and the Preprocessor

3.2.2     Methods

3.2.3     Variables

3.2.4     Files

3.3        Naming XMK Scheduler

3.3.1     Interface definitions and the Preprocessor

3.3.2     Methods

3.3.3     Variables

3.3.4     Files

3.4        3.2.1  Tabs

3.5        Flow Control Statements

 

1       Synopsis

This document defines the C coding standards and style for the XMK operating system.  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 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 XMK. 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 shameless derivative from Shift-Right Technologies’ C++ coding standard (http://www.shift-right.com/openrepo/codingstandard.htm).

2       Standards

The standards documented here are targeted at reducing errors while developing code.  This includes both compile time and run time errors.  No code will be checked into the XMK baseline if it violates these standards.  Of course there are exceptions to every rule, including this one.  Any exceptions must be fully documented and approved by the XMK project administrator. 

2.1      No dynamic memory allocation

While XMK provides dynamic memory interfaces, no dynamic memory allocation is done the kernel or middleware services.  It is an absolute requirement that XMK is not the source of any memory leaks.  Note: This requirement is loosen somewhat when implementing the APL interfaces on a general purpose operating system such as Windows, Linux, etc.  This is due to the fact that APL is running in user space and has limited control of the memory model imposed by the OS.

If kernel services need to create objects dynamically, then the memory must either be supplied by the application as part of the interface or the memory statically pre-allocated at compile time.  When the memory is supplied by the application, the kernel services must provide a method for allowing the application to free the memory once the objects go out of scope.

2.1.1    Allocating static memory

Because of the minimal nature of XMK, global memory allocated by the kernel sometimes needs to be indirectly exposed to facilitate inlining of methods.  For these scenarios, the memory definition is supplied in a private header file.  The following preprocessor construct is used to allocating memory. This convention is use for both the APL Package and the XMK Scheduler.

In the private header file associated with the public interface define a private preprocessor macro that maps to either extern or nothing.  Then prepend this macro to all memory declarations.  The selection of which mapping to use is done by a second private preprocessor symbol.  Then in one of the C implementation files, define the “switch” symbol before including the header file. See the example below.

Example for public Trace Logging interface (src/apl/logging/traceapi.h)

Header file construct (src/apl/logging/trace.h):
/** Macros to manage declarations vs. definition */
#ifdef _APL_ALLOCATE_TRACE_LOGGING_MEMORY
#define DECLARE_
APL_TRACE_LOGGING_MEMORY
#else
#define DECLARE_
APL_TRACE_LOGGING_MEMORY  extern
#endif

/** Message link pointers */
DECLARE_
APL_TRACE_LOGGING_MEMORY AplBool _aplTraceLoggingDisabled;

C file construct (src/apl/logging/trace.c):
/* Ensure memory gets allocated */
#define  _APL_ALLOCATE_TRACE_LOGGING_MEMORY
#include "apl/logging/traceapi.h"

 

2.2      Minimize the public interfaces

Restrict the published interface for a given set of service to the absolute minimum.  Do not expose internal variables or intermediate methods in the public header file.

2.3      Use const wherever possible.

Use the C const qualifier whenever possible.  Every possible modification of data should occur according to explicit relaxation of the default read-only policy. This is especially important when dealing with ROM/FLASH based system since most compiler interpret the const qualifier to mean ‘lives in ROM.’ This is especially true for Harvard architectures and other MCU that differentiate between a pointer to RAM and pointer to ROM. 

2.4      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.  You should always compile with the compiler switch that treats warnings as errors. Note: When dealing a MCU vendor’s compiler this is always not possible.  However, since 95% of all XMK code is not MCU specific, the code must compile without warnings on more standard/robust compilers (aka GCC).

2.5      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”. 

2.6      Conditionally compiled code is not allowed for platform derivatives

Do not use the preprocessor #ifdef/#ifndef constructs to support platform, hardware, and/or 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.7      Protect header file with #ifndef read-once latch

Use the following template for the read-once latch:

#ifndef _dirpath_headername_h_
#define _dirpath_headername_h_

#endif

 

For example: For the header file src/xmk/kernel/kernel.h

#ifndef _xmk_kernel_kernel_h_
#define _xmk_kernel_kernel_h_

#endif

2.8      All header files are compatible with C++

The APL Package and XMK Scheduler are compatible-with/usable-by C++ programs.  This requires that all header files declaring memory and/or function prototypes be bounded with the extern “C” construct.  Typically this only applies to private header files since public header files typically only included preprocessor statements. Use the following template for C++ compatibility.

#ifdef __cplusplus
extern "C" {
#endif

/* Body of header file here */

#ifdef __cplusplus
}
#endif

2.9      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 .c file(s). The #include statements in your header files define the dependency of the file –fewer the dependencies the better.

 

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    Use Doxgyen comments in Header Files

All XMK header files comments are constructed such that the can be easily parsed by doxygen (this include public and private files). Detailed information on doxygen can be found at: http://www.doxygen.org.  The minimum requirements are to use the /** */ construct for commenting.  

3.1.2    No C++ comments

Use of the C++ ’//’ comment operator is not allowed.  This is because not all C compilers for microcontrollers support its use.

3.1.3    Fully documented public header files

All public header files must be completely documented.  This means every public method, constant, compile switch, etc. must be commented.  In addition a description of how to use and not use (i.e. the semantics) the interface should be included.  The public header files are the documentation for XMK.  They must contain all the information a developer needs to use/understand the interface.

3.1.4    Private header files and C files

The quantity/quality of the comments in private header files and C files are left to the individual developers to decide.  However, there is no penalty for being verbose with comments. 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.2      Naming for the APL Package

3.2.1    Interface definitions and the Preprocessor.

The APL package relies heavily on the preprocessor.  All public interface methods are supplied as #define’s.  The supporting private header files are responsible for mapping the public definition to the actual implementation.  In addition, there is a second another level of preprocessor indirection in the private header file. See the example below.

Starting with the interface definition in src/apl/memory/mallocapi.h and the private header file src/apl/memory/malloc.h, this is how the define chain goes.

Public interface

Public to Private mapping

Indirection in private file

Apl_initMalloc

_Apl_initMalloc

Apl__initMalloc

 

 

 

Naming Rule:

add leading ‘_’.

No leading ‘_’, but added ‘_’ in the middle.

Why two levels of indirection? The first level allows the implementation to be separated from interface definition, i.e. it defers whether the method is a macro or a true function to the "implementation".  For example, depending on the options/switches selected, Apl_initMalloc maps to nothing when using the C library, or the method void Apl__initMalloc( AplSize_t n ) when not using the C library.  The second level allows for future specialization, somewhat akin to C++ inheritance. For example, let's take the case of Apl_initMalloc when not using the C standard library.  In this case the mappings are as above.  Now let’s say we have a new optional requirement that requires additional code for the body of Apl__initMalloc.   The first step is to write a new function, say Apl__initMalloc2, which contains the new code.  As we are writing Apl__initMalloc2 we notice that we are replicating all of the code from Apl__initMalloc.  So instead of copy-paste the code we do the following:

void Apl__initMalloc2( AplSize_t n )
    {
    /* Call 'parent' class to setup the heap */
    Apl__initMalloc(n);

    /* do extra stuff here */
    ...
    }

 

The next step is to change the mapping of the preprocessor macros to:

Apl_initMalloc  à _Apl_initMalloc  à Apl__initMalloc2

3.2.2    Methods

The APL package naming follows a C++’ish paradigm in that interfaces are identified by the “namespace” and “class” names. Public methods use the following format: Nnn_cccMnn

Where Nnn is the namespace of the method.  The namespace identifier may contain nested namespaces. At minimum the namespace must be Apl for non MT safe code, and Aplmt for MT safe code.

Where ccc is the class name of the interface.

Where Mnn is a public method of the interface/class

Examples:

Interface: Single Link List.

Namespace: Apl
Class name: slist
Methods:
    void  Apl_slistInitialize( AplSList* listToInitialize );
    void* Apl_slistGet( AplSListHdl fifo );

 

Interface: Network Application for the uIP Network Stack

Namespace: Aplmt::UIP
Class name: inet
Methods:
    void AplmtUIP_inetInitialize(void);
    void AplmtUIP_inetMain(void* notUsed);
    AplmtThreadHdl AplmtUIP_inetGetThreadHdl(void);

 

Note: There are numerous exceptions to this rule such as the memory and file descriptor interfaces.  However, the goal is to have all new interfaces conform to this convention.

3.2.3    Variables

3.2.3.1  Function arguments

Camel caps style is used.  Function arguments names start with a lower case letter and then capitalize the first letter of the ‘next word’.

Examples:

void  Apl_slistInitialize( AplSList* listToInitialize );
void AplmtUIP_inetMain(void* notUsed);

3.2.3.2  Local variables

Local variables follow the same convention as function arguments.

3.2.3.3  Global variables

There shall not be any public global variables. 

File global variables (i.e. declared as static in the C file) following the same convention as function arguments, but are prefixed with a leading ‘_’.

Internal global variables follow a naming convention vary similar to public methods.  Private global variables use the following format: _nnnCccVvv

Where nnn is the namespace of the method.  The namespace identifier may contain nested namespaces. At minimum the namespace must be apl for non MT safe code, and aplmt for MT safe code.

Where Ccc is the class name of the interface.

Where Vvv is the variable name in camel caps.

Examples:

/* Global variable. Namespace: aplmt, class=DevUartA */
AplmtThreadHdl _aplmtDevUartARxWaiter; 

/* File global variable */
int _myCounter;

3.2.4    Files

In general use verbose file names – do not restrict file naming to an 8.3 naming convention. 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.

Only use lower case file names. This avoids problems when dealing with multiple host platforms and file systems that are not case sensitive.

Do not use ‘-‘ in file or directory names.  This is a least-common-denominator requirement in that some microcontroller chip vendors’ compiler toolchains do not handle ‘-‘ in file and directories names.

3.2.4.1  Single threaded code

All non multi-threaded safe code must be located under the src/apl directory tree.

3.2.4.2  Multi-threaded code

All multi-threaded safe code must be located under the src/aplmt directory tree.

3.2.4.3  Header files

Public header files must end with the suffix api.

Examples.

src/apl/containers/slistapi.h
src/aplmt/networking/uip/inetapi.h

Private header files are typically named after their corresponding public header file sans the api suffix.  Header files name do not need to be unique across the entire source tree – only unique within their containing directory.

3.2.4.4  C/ASM Files

It is recommended, but not required, that C/ASM file names are unique across the source tree.  This is because numerous IDE’s (especially those based on Visual Studio) by default only use the file name, ignoring the path, of tracking C/ASM files – i.e. do not allow duplicate file name even if the files are in different directories.

3.3      Naming XMK Scheduler

3.3.1    Interface definitions and the Preprocessor.

The XMK Scheduler relies heavily on the preprocessor (even more so than APL).  All public interface methods are supplied as macro definitions.  The supporting private header files are responsible for mapping the public definition to the actual implementation.  The internals of the private header files get a bit “messy”.  This is due to the interdependencies of the kernel services and respect to the actual hardware and compile time options.  The determination of what maps to what is greatly determined by the src/xmk/config/resolve.h header file.  See XXX for details about resolve.h. The basic guideline for the public interfaces is to map all constants, symbols, types, functions, etc. to the a “private” label that is the same name, but with a leading ‘_’.  See the example below.

/** Maximum number of threads that can be created/active at any
    one time */
#define XMK_
MAX_THREAD_COUNT            _XMK_MAX_THREAD_COUNT

/** NULL Thread Handle */
#define XMK_NULL_THREADHDL              _XMK_NULL_THREADHDL

/** This method initializes the kernel prior to any kernel calls
    being made
    Prototype:
        void Xmk_initialize(void);
 */
#define Xmk_initialize()                _Xmk_initialize()

3.3.2    Methods

All methods use the following format: Xmk_nnn.  Where nnn is the function name.  Camel caps style is used for nnn, starting with initial lower case letter.

Examples:

XMK_TSD_KEY     Xmk_requestTsdKey(void);
XMK_TSD_ELEMENT Xmk_getTsdElement( XMK_TSD_KEY index );

3.3.3    Variables

3.3.3.1  Function arguments

Camel caps style is used.  Function arguments names start with a lower case letter and then capitalize the first letter of the ‘next word’.

Examples:

void Xmk_signal( XMK_THREADHDL threadToSignal );
Xmk_fatalError(XMK_BYTE errorCode);

3.3.3.2  Local variables

Local variables follow the same convention as function arguments.

3.3.3.3  Global variables

There shall not be any public global variables. 

File global variables (i.e. declared as static in the C file) following the same convention as function arguments, but are prefixed with a leading ‘_’.

Internal global variables follow a naming convention vary similar to public methods.  Private global variables use the following format: _xmkVvv

Where Vvv is the variable name in camel caps.

Examples:

/* Global variable */

XMK_BYTE _xmkKrnStatus;

/* File global variable */
int _myCounter;

3.3.4    Files

In general use verbose file names – do not restrict file naming to an 8.3 naming convention. 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.

Only use lower case file names. This avoids problems when dealing with multiple host platforms and file systems that are not case sensitive.

Do not use ‘-‘ in file or directory names.  This is a least-common-denominator requirement in that some microcontroller chip vendors’ compiler toolchains do not handle ‘-‘ in file and directories names.

3.3.4.1  Header files

All Public header files are placed in the src/xmk/ directory.

All private header files are contains in subdirectories from the public directory. Header files name do not need to be unique across the entire source tree – only unique within their containing directory.

3.3.4.2  C/ASM Files

It is recommended, but not required, that C/ASM file names are unique across the source tree.  This is because numerous IDE’s (especially those based on Visual Studio) by default only use the file name, ignoring the path, of tracking C/ASM files – i.e. do not allow duplicate file name even if the files are in different directories.

3.4      3.2.1  Tabs

Tabs stops will be set to 4!  The actual use of spaces vs. tabs when indenting is left up to the individual. When modifying someone else’s code – follow the original author’s tab style – whether or not it agrees with your own personal preference.

3.5      Flow Control Statements

The flow control primitives if, else, while, for and do must be followed by a block, even if it is single statement or empty block.  This is because XMK makes extensive use of macros and it is imperative that brackets are always use for control flow blocks.  Examples:

while( /* do something */ )  // Not allowed
    *dst++ = *src++; 

while( /* do something */ )  // Good
    {
    *dst++ = *src++; 
    }
 
if( isOpened() )      // Not allowed
    foobar();

if( isOpened() )      // Good
    {
    foobar();
    }


[Home] [Intro] [APIs] [Source] [Platforms] [Licensing] [FAQ] [News] [Acknowledgements][Site Map]
Send mail to webmaster@shift-right.com with questions or comments about this web site.
Copyright © 2004 Shift-Right Technologies, LLC
Last Modified: Friday, November 26, 2004