User Tools

Site Tools


coding_standards

Coding Standards

Introduction

There are many good reasons for adopting a defined coding standard, especially in an increasingly expanding software development environment. This wiki page outlines the expected standards to which C source will be written by a DTVKit member, and discusses the layout and techniques that should be integrated into all future work carried out during software development. The expected attributes for software written under this standard are as follows:

  • Readable – whereby variable and function names, code layout, and commenting contribute to ensure effortless accessibility upon subsequent review (particularly by other developers,) and results in the minimal time being spent on familiarisation with the design aspects of the code.
  • Maintainable – if code is written to a formalised regime then it lends itself to successive updates, fixes, and enhancements, with minimal complications.
  • Debuggable– when assignments and mathematical equations are kept in elementary components, and code is not compacted onto single lines unnecessarily, it makes the process of ‘stepping through’ code during debugging (and analysing the performance of the software) far more efficient.

In summary, this coding standard exists to ensure the maximum proportion of time spent by any software engineer on a particular component is spent making effective alterations, not spent deciphering ambiguous source code! Unless otherwise specified, then any methods outlined in this document are rules that should be strictly adhered to when developing fresh code.

Limitations of a coding standard

Due to the significant amount of third-party software component integration that inevitably occurs during the development of a product (especially within an embedded environment,) there are always circumstances when the interfacing code will have to adhere to the protocols and standards of the supplied libraries etc. Therefore it is sometimes impossible to ensure that code written at this level will always meet the standards we set ourselves. The imperative in such situations is that any code written to comply to alien standards should have comprehensive documentation, either coherent in-code remarks or a supplementary document, archived alongside the project.

Coding environment

Some of the issues regarding working to a convention are related to the choice of platform within which source code is written and edited. Generally such issues do not concern an individual programmer; often only one IDE can be used to perform work on a specific project anyway. The basic requirements for source code, irrespective of language, which are related to the editor tool used are:

  • The maximum line length shall be 120 characters, but producing readable code is to be considered a higher priority so a line can exceed this if breaking the line before 120 characters would defeat this objective.
  • No hard tabs are permitted; soft tabs of three spaces are required.
  • The source should not contain any extended ASCII characters, or any special control sequences that may cause compilation problems.

These requirements can be implemented by ensuring that suitable development tool(s) are configured correctly.

Text editors

This convention does not stipulate that a specific editing tool must be used, although some basic requirements of such an application are as follows: The editor is able to save the source code in raw ASCII. For this reason high-end word processors such as Microsoft Word should not be used. This also excludes any editor that is specifically geared to work in the Rich Text formats, such as Microsoft WordPad. The editor does not implant hard tabs into the code. This is because the level of indentation used to represent such tabs is not universally defined, resulting in erratic indentation when code is viewed using different tools. This may also cause problems when analysing file differences when an archiving tool is merging software changes. Most editors can be configured to implant multiple spaces instead of hard tabs (this is called ‘soft tabbing’) and your editor should be configured to implant three spaces per tab in this case.

Many editors allow for any hard tabs to be represented in the display as a configurable alternative ACSII character. Using this mechanism can be useful in loading in an existing source file, as it will become immediately apparent if hard tabs are present the code, so these can be removed quickly.

The editor is configured not to perform word-wrap during editing as this may give a false impression as to the layout of any source code. If the coding convention is adhered to then excessively lengthy lines should not occur anyway!

Code conventions

Project Files

File topology

Code should be arranged in an orderly fashion within files for a given project. These files are expected to fall into two categories ‘source’ files, in which the body of code components are defined, and ‘include’ files, which give prototypes and references to functions within the associated source file(s), depending on the coding language. All functions and data definitions required to service a particular component of a project (whether it is a hardware peripheral, or merely a software engine,) should be grouped together. This should be reflected in the filenames used, the aspects of any commenting, and perhaps in the combined interfacing to other modules through a shared include file. At the beginning of each file, a comment header shall be included which provides information regarding revision date, revision number, description of the file, and revision history. This header shall also contain a copyright notice and a message regarding the confidential nature of the content. (See Appendix A – File comment headers)

Source files

Source files contain the body of the code for a given module, and can be broken down into parts, which must appear in the following sequence:

  • Comment header - used to provide comprehensive information regarding the file and its purpose. (See 3.5.1 - File headers)
  • File inclusions – where reference to other modules can be made. (See 3.1.3.3 - File inclusions)
  • Pre-processor constant definitions – specified by using the #define keyword, these can represent numeric substitutions, or be macros.
  • Local enumerations, structures, and type definitions – always defined using the static keyword, used to manipulate local data in the module. (See 1.1.1 - Type definition names)
  • Local (static) variable declarations – always defined using the static keyword, with declared initial values if required. (See 3.2.1 - Variable names, 3.5.4 - Variable comments, 3.4.4 – Variable initialisation)
  • Local function prototypes – each local function must have a corresponding prototype defined. (See 3.2.2 - Function names)
  • Global function definitions – defined without using the static keyword, allowing access from other modules. (See 3.1.4 - Function layout, 3.2.2 - Function names). A comment header outlining the design must immediately precede each function. (See 3.5.3 - Function Headers)
  • Local function definitions – always defined using the static keyword hence can only be used by other functions within this source file. (See 3.1.4 - Function layout, 3.2.2 - Function names). A comment header outlining the design must immediately precede each function. (See 3.5.3 - Function Headers)

It is important to note that no data variables defined within a module should be explicitly accessible from other modules – hence the definition of any such variable (outside of all functions) which is declared without using the static keyword is strictly prohibited. Such entities (known as Global variables,) are fully supported by, and are accessible within the C/C++ programming languages, but the use of them is usually the result of undisciplined interface implementations.

Include files

Include files contain references to globally-accessible attributes to associated source file(s), and can be broken down into the following parts, which must appear in the following sequence:

  • Comment header - used to provide comprehensive information regarding the file and its purpose. (See 3.5.1 - File headers)
  • Pre-processor constant definitions – specified by using the #define keyword, these can represent numeric substitutions, or be macros.
  • Global enumerations, structures, and type definitions – always defined without using the static keyword, used to interface with the functions defined, and to manipulate any data traffic from the relevant modules.
  • Global function prototypes – defined without using the static keyword, allowing access to the function from other modules. If the prototype is for an API function (ie accessible via external software packages) a description header is required. All function prototypes must include the names of all parameter variables. Further, any functions that receive no parameters must be declared with a void argument list and not just left blank. (See 3.2.2 - Function names)

It is important to note that no variables defined within a module are explicitly accessible from other modules – the use of the extern keyword to declare any data variable is strictly prohibited.

For many project architectures, it may be necessary to access data from variables that are maintained within the module. This is achieved by offering some interface function, which imparts the relevant settings, or offers some means of adjusting such data. By servicing relevant variables that are enclosed within the module in this way, the code designer can ensure that such data is only accessible within a specified domain.

File inclusions

When files are included in a source, care should be taken to arrange the inclusion directives into a clear and concise order. File inclusions that are explicitly related to the compiler and the operating system should all appear in the first block. Each file name is enclosed within ‘angle brackets’. Thereafter should appear other file inclusions in a second block; these will be specific project module inclusions.

/* System inclusions, so checked in the global include paths configured for this project */
#include <stdio.h>
#include <memory.h>
#include <rtos.h>

/* Other inclusions, so explicit paths may be defined */
#include “screen.h”   // GUI components for screen display
#include “events.h”   // UI event handler for keypad interaction(s)
#include “config.h”   // Configuration data inteface (from NVM)

Function layout

A function’s content will be arranged in the following sequence:

  • Comment header - used to provide comprehensive information regarding the function and its purpose. (See 3.5.3 - Function Headers)
  • Function definition – which must match identically the pre-defined prototype. The parameter list will not be defined in the older style of declarator mechanism; hence the parameters must be named within the declaration parenthesis. If a function has no parameters then void must explicitly be declared
  • Static variable definitions – defined with initialisation values, if required. Being non-volatile these variables have the largest impact of the data footprint associated with a function, so are declared first to emphasise this. (See 3.4.4 - Variable initialisation)
  • Automatic variable definitions – with same type variables being defined grouped together if sensible. Comments defining each variable’s use may also be included (See 3.5.4 - Variable comments). For languages such as C++, variable definitions can occur when the variable is first used.
  • Function entry declaration macros – before any code is defined, this mandatory macro must be called. This will, for example, prepare some additional debug analysis of the function’s performance, and possibly provide information for function profiling. (See 3.4.3.1 - Function entry / exit declarations)

Assertion macros – used to determine if supplied parameters have valid content, for example.

  • Code body – the code of the function. Data offset indexes, and pointer parameters shall always be checked for validity. If invalid parameter values are found then an error flag / variable should be set and entry to the main code body should be prevented. No “return” shall be called from a parameter check.

Function exit declaration macros – before return is called, this mandatory macro must be called. This will, for example, provide some additional debug analysis of the function’s performance when executed. (See 3.4.3.1 - Function entry / exit declarations)

  • Call to return – if required.

The use of multiple exit points from a function should be avoided. Code can usually be written in such a way as to require that a single return call be made at the tail of the code.

File size

There are no absolute restrictions that can be opposed on the maximum size of source code files, but the larger a file becomes during development, the more difficult it becomes to navigate the content in a coherent fashion during editing. It is recommend that source files are broken up into more sensibly sized modules whenever possible.

Object names

The general rule for any name is to be descriptive and concise. Before naming any object (variable, function, macro, etc.), the programmer must carefully consider the purpose of the object. The name shall be descriptive enough to indicate the purpose of the object and to prevent the object being confused with some other object. If an object evolves to have a different purpose than the original purpose, then the name of the object must be changed to reflect its new purpose. Abbreviations shall be avoided as much as possible unless the full name becomes excessively long. Furthermore, every effort should be made to keep abbreviations consistent throughout all source code. Acronyms are far more acceptable, being represented in either uppercase or lowercase. All names must reflect a professional attitude and an understanding of the problem that was solved by the code. Names that imply that some problem was fixed without being fully understood are not allowed, as are those that imply a casual attitude. Remember that third parties will get access to our code. Numbers must not be used in place of words in function and variable names. For example, “Convert2Hex” and “Work4Food” are not allowed, as they are a little cryptic, and may prove confusing.

Variable names

A variable name must be entirely lower-case format, optionally using the underscore character to grammatically separate the words that make up its description. The first and last character of a variable name should not be an underscore. Variables defined globally within a module must have unambiguous (multi-word) names, as these are accessed by several functions within that module.

ExampleValid?
MyVariable No - uppercase characters are not permitted
MYVARIABLE No - uppercase characters are not permitted
_my_variable No - leading or trailing underscore characters are not permitted
myvariable Yes - although ‘my_variable’ would be more readable.
my_variable Yes

When defining a variable at the top of a module you will be expected to furnish it with some commenting regarding it’s purpose anyway. But it’s important to remember that if you have to keep referring to these comments when trying to analyse the variable’s use in any of the subsequent functions, then it’s likely that the variable’s name isn’t expressive enough in the first place!

Automatic (non-static) variables defined within a function fall into two categories:

Utility variables - generally used for temporary values in algebraic sequences, and for loop counters. These are often represented with single character names, and so impart no information as to their use through their definition.

Historically coders have used names like i, j, k etc. for general loop counters, ptr as a utility pointer type, and names like cnt for more complex iteration counters. The key issue here is be sensible! If you find yourself having to define more than a handful of utility variables within a function you should ask yourself whether your code lends itself to be broken into several sub-functions instead.

Specific variables – used to represent some specific condition within the code section. These should have meaningful (perhaps multi-word) names, particularly those used in conditional code.

Member variable names

For object oriented languages, such as C++, member variables must follow the above variable naming convention with the addition of a preceding “m_” to indicate it’s a member variable.

Function names

There are two distinct types of functions:

Local functions – these have a scope local to the module in which they are implemented, and are defined as static functions. Their names shall be defined in mixed-case format, such that each word has its first character capitalised. The use of underscore is forbidden.

Examplevalid?
my_function No - underscore characters are not permitted
myfunction No - the leading characters in the component words of the name should be upper-case
MYFUNCTION No - only leading characters in the component words of the name should be upper-case
MyFunction Yes

Global functions – these have prototypes in an associated header file, and so can be accessed by other modules within the project. Their names have two distinct parts separated by an underscore character. The first part is an upper-case abbreviation which identifies the module (or library of modules) where the function is found. The second part is defined in mixed-case format, such that each word has its first character capitalised. The use of more than one underscore in the entire name is forbidden.

ExampleValid?
bld_myfunction No - the characters in the first word must all be upper-case
BLD_myfunction No - the leading characters in the component words of the name should be upper-case
BLD_MYFUNCTION No - only leading characters in the component words of the name should be upper-case
BLD_MyFunction Yes

Type definition names

The type definition facility (for example, represented using the typedef keyword in C) is used to create meaningful synonyms for objects within the code. A type definition name must be entirely upper-case format, optionally using the underscore character to grammatically separate the words that make up its description. Variants within this naming convention exist, as type definitions fall into three distinct categories, dependant on the location of such definitions:

System type definitions – these are the basic aliases which underline the fundamental data types used throughout projects. They are most probably defined in the techtype.h header file, which should be included in all modules. Their names adhere to the format described above.

Valid examples

BOOLEAN   ,   S8BIT   ,   U16BIT

Global type definitions – these are aliases used to define data types used in the interface to functions from another module (or library of modules.) As an extension the basic format they should all begin with two extra underscore-separated words. The first is an upper-case letter used to denote what kind of entity the type definition refers to. The options are as follows:

LetterRefers to…Example
SStructure
 typedef struct my_structure {
     U16BIT mask;
     BOOLEAN flag;
} S_GUI_MY_STRUCTURE ;
EEnumeration
typedef enum my_enumeration {
   INITIAL_STATE,
   NEXT_STATE,
   LAST_STATE
} E_HARDWARE_MY_ENUMERATION ;

The second part is an upper-case abbreviation which identifies the module (or library of modules) where the function is found.

Local type definitions - these are aliases used to define data objects used within functions within a single module. As an extension the basic format they should all begin with an upper-case letter used to denote what kind of entity the type definition refers to. The options are as follows:

Letter Refers to… Example
S Structure
typedef struct my_structure {
     U16BIT mask;
     BOOLEAN flag;
} S_MY_STRUCTURE ;
E Enumeration
typedef enum my_enumeration {
   INITIAL_STATE,
   NEXT_STATE,
   LAST_STATE
} E_MY_ENUMERATION ;

Structure names

The convention used when naming a structure is identical to that of a variable. The additional use of a type definition is dependent upon the location of the structure definition as follows:

System structures - these are the basic data structures, which are often parameters passed to fundamental functions used throughout projects. They are most probably defined in the techtype.h header file, which should be included in all modules. These structures must have a type definition; they should never be referred to literally. The type definition used must be a system type definition (See 1.1.1 - Type definition names), with the body of the name being identical to that of the structure name.

Valid examples

typedef struct link_list_header {
        U8BIT *prev_ptr ;
        U8BIT *next_ptr ;
} LINK_LIST_HEADER ;

Global structures - these are defined within a module header file and can be accessed by functions outside of the native module. These structures must have a type definition; they should never be referred to literally. The type definition used must be a global type definition (as defined in section 4.3), with the body of the name being identical to that of the structure name.

Valid examples

typedef struct gui_config {
        U16BIT max_x_pixels;
        U16BIT max_y_pixels;
} S_GUI_CONFIG ;

Local structures – these are defined within a module and are only accessed by functions within that module. It is at the developer’s discretion as to whether these structures require a type definition; they may well be referred to literally. If a type definition is used it must be a local type definition, with the body of the name being identical to that of the structure name. Valid examples

struct func_params {
      U16BIT iterations;
      U8BIT   buffer[ MAX_BUFFER ];

} ;

typedef struct func_params {
      BOOLEAN  completed;
      U16BIT position_offset;

} S_FUNC_PARAMS ;

Constant names

Constants can be defined within the source by one of two methods:

pre-processor macro constant, defined using the #define keyword. This is the preferred method, as it has no data payload on the compiled code if the constant is not referenced.

static variable can be defined using the const directive. Used if the code insists that a constant is used as a specific data type. This will also result in some data payload on the compiled code In either case, a constant name must be entirely upper-case format, optionally using the underscore character to grammatically separate the words that make up its description. Variants within this naming convention exist, as constants fall into three distinct categories, dependant on the location of such definitions:

System type constants – these are the basic values used throughout project code. They are most probably defined in the techtype.h header file, which should be included in all modules. Their names adhere to the format described above. Valid examples

TRUE   ,   YES   ,   INVALID_HANDLE

Global type constants – these are values used in the interface to functions from another module (or library of modules.) As an extension the basic format they should all begin with an extra underscore-separated word. This header word is an upper-case abbreviation which identifies the module (or library of modules) header file where the definition is found. Valid examples

GUI_MAX_RESOLUTIONS   ,   AVD_SWITCH_OFF

Local type constants - these are values used within functions (and defined) within a single module. They adhere to the basic format outlined above Valid examples

PROCESS_FINISHED  ,   ACTION_COMPLETED

Macro names

Macro are defined within the source using pre-processor directives, using the #define keyword. A macro name must be entirely upper-case format, optionally using the underscore character to grammatically separate the words that make up its description. Variants within this naming convention exist, as macros fall into three distinct categories, dependant on the location of such definitions:

System type macros – these are the basic macros, which are used throughout projects. They are most probably defined in the techtype.h header file, which should be included in all modules. Their names adhere to the format described above. Valid examples

MAX( ) , GET_HEADER( )

Global type macros – these are used in the interface to functions from another module (or library of modules.) As an extension the basic format they should all begin with an extra underscore-separated word. This header word is an upper-case abbreviation which identifies the module (or library of modules) header file where the definition is found. Valid examples

GUI_FREE( )   ,   AVD_ACTIVATE( )

Local type macros - these are used within functions (and defined) within a single module. They adhere to the basic format outlined above

Valid examples

STOP( )   ,   GET_WHOLE_VALUE( )

File names

The convention used in defining textual content of the prefix to a filename is at the developer’s discretion, but a method that imparts some sensible information regarding the module’s content is advised. Using some overtly cryptic name is not acceptable. Do not use filenames that are totally irrelevant to the module! Underscores may be used in any filename.

Code Presentation

Use of spaces for clarity

To ensure maximum readability on the source, the following rules apply when using spaces in the code:

Between operators – spaces shall be used to separate all tokens within an assignment or conditional statements. The only exceptions are for tokens immediately following or preceding an opening or closing parenthesis respectively, and between any variable and a pre-increment/post-increment type operator.

Between parameters – spaces shall be placed directly after each comma used to separate tokens in a function prototype.

Preceding code terminators – there is no requirement to insert a space before each semi-colon used to terminate a line of source code.

Use of braces

Arranging code to the convention outlined below ensures maximum clarity, and minimises the potential for misinterpretation by the software developer. The following rules apply without exception:

  • All conditional code shall be contained within a block (i.e. using braces).
  • Any open brace shall appear on a line by itself, with the same indentation as the preceding line.
  • Any close brace shall appear on a line by itself, with the same indentation as its corresponding open brace.
  • All code within such a section will be indented one level from that of the enclosing braces

Use of parentheses

Parentheses shall be used in algebraic formulae for clarity, even when not explicitly required by the compiler. Such a regime results in the minimal time is expended analysing the correct operator precedence of a compiler environment when analysing the mathematical outputs of such equations.

Additional requirements

C/C++ Language exclusions

The Concise conditional expression

The use of the conditional expression mechanism (conditional ? assignment : assignment) should be used sparingly and only in a way that means the code is still easily readable, as this is often abused, resulting in less coherent code. A typical use would be when it is used within the parameter list of a function in the print family to effect the output text of such a function. In most cases such calls are made within an embedded project as part of some form of debug output, and so are not likely to be part of the final build.

Goto statements

The use of goto within a function body is prohibited. If it appears to be a necessary requirement to use this mechanism when writing a section of code, then the code needs to be redesigned.

Extern statements

The use of an extern declaration is strictly prohibited. It is not necessary to use the keyword when prototyping functions within an ‘include’ file, and the prototyping of a function directly from within one module to link to a function within another is also not permitted. The extern declaration should never be used to declare a variable, as all variables can only have a local scope within each module.

Comment styles

The use of C++ comments (whereby the comment text is surrounded by the /* … */ delimiters) is to be used when commenting C code. This generates portable code.

The single line C++ comment can be used in C++ code.

Combined assignments

The implementation of C code that includes combined assignments is prohibited. Such mechanisms can prove difficult to analyse during debugging, and do not make for the most readable of code. The two main variants of this method are:

Combined variable assignments – such as when assignments are chained together, or when the pre / post increment operators are used in an assignment.

Invalid examplesValid equivalent code
a = b = c;b = c; a = b;
a = b++;a = b; b ++;

Such examples demonstrate that the equivalent code if far clearer to decipher, and is free from any possibility of misinterpretation of operator precedence.

Assignments within function calls – whereby assignments are inserted into the passed parameter list of a function execution.

Invalid examplesValid equivalent code
retval = MyFunction( ++index );
      retval = MyFunction( index );
	index++; 
retval = MyFunction( index++ );
retval = MyFunction( index );
index++;
MyFunction( a = b )
	a = b;
MyFunction( a );

In addition to the above reasons why these equivalent function calls are preferable, the possibility exists that a macro substitution for any function call may be implemented (perhaps for test purposes to analyse the function’s performance,) at a later date. In this scenario such assignments could result in erroneous code substitutions.

Assignment within a conditional

The implementation of code that includes assignments implanted into conditional statements isn’t prohibited but the requirement that code should be easy to read takes precedence, so in the following examples the first one shouldn’t be used, but the second one is often seen and is acceptable. As always, great care should be taken with bracketing and brackets must be included to make the order of precedence clear..

Invalid examplesValid equivalent code
if( ++a >= MAX_VALUE)
{
    …
}
a++; 
if (a >= MAX_VALUE)
{
    …
}
if( (ptr = malloc(size))  != NULL)
{
    …
}
ptr = malloc(size);
if(ptr != NULL)
{
    …
}

Such examples demonstrate that the equivalent code if far clearer to decipher, can be analysed far more effectively when debugging.

Another very good reason why such code should be avoided is the fact that this method creates an environment that facilitates the sort of erroneous use of assignment equality (represented by = ) where a conditional equality (represented by == ) is required. All experienced programmers have at some time fallen foul of this problem, and should encourage any convention that actively avoids the possibility of it occurring!

Implicit conditionals

The implicit conditional testing of expressions is prohibited. In all examples a conditional comparator must be present in the code, as this ensures total readability is achieved

Invalid examplesValid equivalent code
cnt = 100;
while(cnt)
{
    …
    cnt--;
}
cnt = 100;
while(cnt > 0)
{
    …
    cnt--;
}
ptr = malloc(size);
if(ptr)
{
    …
    free(ptr);
}
ptr = malloc(size);
if(ptr != NULL)
{
    …
    free(ptr);
}

This rule applies to all data types and the direct analysis of the return value from functions, with the sole exception of the BOOLEAN type. (See 3.4.2.3 - Boolean operations) The values defined to represent the TRUE and FALSE states of this data type definition are guaranteed to be valid for such implicit tests.

Language extensions

Defining infinite loops

Infinite loops are implemented with while(TRUE) and this should be promoted as the only allowed infinite loop mechamism:

Method
while ( TRUE )
{
   …
}
Using variable types

The explicit reference to the native C integer variable types (represented by char, int, and long, with the additional signed / unsigned prefixes,) is not permitted. The data types represented by these types is platform-specific, so their use would introduce portability issues if used explicitly. The only exception to this rule is when explicitly casting variables parameters being passed to third-party functions. (See 3.6.2 - Casting of variables to third party functions) Reserved type definitions representing the range of required integer variable types is defined in the techtype.h as follows:

Valid examples

U8BIT , S8BIT , U16BIT, S16BIT , U32BIT , S32BIT

These will be defined to have equivalent representative bit sizes for every development environment.

Boolean operations

Most C developers eventually define some mechanism to embody the Boolean logic operator not supported in the ANSI standard. The type definition BOOLEAN is defined in the techtype.h header file, along with the assignment / test values TRUE and FALSE. The two values associated with this data type are guaranteed to be:

Logically opposite – the use of the logical not (!) operator will invert the sate of a BOOLEAN variable.

Representative of C logic– ensuring that use in conditional statements will have the desired effect.

The native data type used for the BOOLEAN type definition is undisclosed, but will be chosen to be the most natural width for the processor platform within a given project.

Debug macros

Function entry / exit declarations

All functions have to be written to contain logging facilities. This will allow for the analysis of the function’s performance, and possibly provide function-profiling information.

MacroDescription
FUNCTION_START( function_name ) ;
Outputs a text message reporting that the given function has started processing. This should be included at the start of all executable code, and before all other debug macros are used. The <function name> is an explicit reference to the name – double quotes are not required.
FUNCTION_FINISH( function_name ) ;
Outputs a text message reporting that the given function has finished processing. This should be included in the code before every return statement, or as the last line of code of the function body if no calls to return are made. The <function name> is an explicit reference to the name – double quotes are not required.

The use of these macros is mandatory. They must be used in the manner specified above.

Assertion

Conditional assertion is a debugging method that gives the developer the capability to check the state of variables (by way of a test expression) at run-time to ensure that no critical errors occur as a result of invalid data settings.

MacroDescription
ASSERT( expression ) ; Outputs a text message reporting if the assertion fails to the debug message handler. The <expression> can be any section of syntactically valid C code.

The use of these macros is mandatory. They must be used in the manner specified above. The macro is not just for checking correct parameters passed to functions - it can also be used as many times as required throughout code to analyse any other functionality.

Macro utilisation

Each type of macro can be activated by defining the following pre-processor constant:

Macro(s)Mandatory inclusion?Activation constant(s)
FUNCTION_START,FUNCTION_FINISH Yes DEBUG_FUNCTION , DEBUG_ALL ,
ASSERTYesDEBUG_ASSERT ,DEBUG_ALL ,

Variable initialisation

All variable declarations, whether local or global, must not include an initialisation value. This is due to the mechanisms by which a compiler will ascertain the position of variables within the memory map, or the processing payload required in initialising data.

The alternative methods are as follows:

Local variables – created on every instance of the function being called, and until explicitly initialised, their value is undefined. They should be initialised within the code section of the function, and should only be assigned values at the point in the code immediately before where they are first referenced.

Global variables – assigned a location within a nominated data section, the content of which is initialised during program initialisation. If defined using the const directive, then an initialisation value must be supplied- even if a zero value is required. If defined as non-constant, then an initialisation is only required when a value other than zero is needed. They should be explicitly initialised from within a specific function within the module. It is permissible for code to assume that such variables are set at start-up as follows:

Variable typeInitialisation value
U8BIT , S8BIT , U16BIT, S16BIT , U32BIT , S32BIT 0 (zero)
U8BIT* , S8BIT* , U16BIT*, S16BIT* , U32BIT* , S32BIT* Any other pointer type NULL
BOOLEAN FALSE

When defining a group of associated global variables, to avoid using initial values that are non-zero, supply an initialisation function which sets these variables specifically. This saves on the data footprint of the program, and allows for the re-initialisation of a module at any time.

Commenting Methods

Comprehensive commenting throughout the code is essential to minimise the learning curve during future familiarisation with a project. This is true for both developers who are reviewing previously developed code, and those who are new to the code. This section serves to outline the correct mechanisms to ensure that correct commenting procedures are maintained throughout the development cycle of a project. When composing comment texts it is essential that clear and grammatically correct English shall be used. This further ensures that reference to commenting by future developers (some of which may potentially not be native English speakers,) are able to understand the comments more easily.

Comments fall into two categories:

Same line comments – found after the code component of a line. These should be separated from the code by using a sensible level of white space to ensure clarity.

Multi-line comments – whereby the comment text is too lengthy to be placed on a single line. These should always precede the code content that they relate to, and should be separated from the code by a single blank line for further clarity.

When adding comments to your code, if the only possible content seems to restate the obvious, then try to consider any background information regarding the code structure. Generally speaking, there is always something to say about every aspect of code when imparting information for future reference.

File headers

At the start of all project source files a header comment must be present. These should be edited to provide comprehensive information regarding the file. (See Appendix B – File comment headers)

Pre-processor comments

Every #include used for a specific project module inclusion (i.e. whereby the file name is enclosed within ‘double quotes’) can be accompanied by a comment that indicates the purpose of the included file. This is often added as a same-line comment. Every #define used to define a constant value used within the code can be accompanied by a comment that indicates the manifest reason for the value encapsulated. This is often added as a same-line comment.

Function Headers

Directly preceding each function declaration within a source file a header comment must be present. These should be edited to provide comprehensive information regarding the function. (See Appendix C – Function comment headers). For externally referenced functions (API functions), function headers should also be attached to the function prototype declarations in the header file, thus enabling engineers supplied with only the header files to understand the functionality of an API.

Variable comments

Variables defined within a module (both global or static declarations) must be accompanied by a comment that indicates the purpose of the variable, and if possible, the functions that service it. Automatic (non-–static) variables defined within a function fall into two categories:

Utility variables - generally used for temporary values in algebraic sequences, and for loop counters. No commenting is required for such variable definitions. Specific variables – used to represent some specific condition within the code section. These should have comments that describe the purpose(s) of the variable within the function’s process.

Code comments

As a general rule comments are expected to describe why a particular action is being taken, rather than what specific action is being taken within a section of code, unless that action is not evident from reading well-written code. It shall never be assumed that the next person who looks at the code will understand what the original programmer was doing or that code is necessarily self-commenting. It is sometimes difficult to quantify to what extent a section of code’s functionality is self-evident, and how much additional commenting may be required. Avoid integrating too much commenting into a code section before code has been tested; this will prevent commenting having to be significantly re-written should the code need alterations. Furthermore, most people write more coherent comments when analysing work retrospectively.

When coding and commenting, imagine yourself not looking at the code for several months, and then attempting to remember how it worked.

Archive comments

Any chance offered by archiving tools to supply comments when checking-in code must be utilised. Such comments must describe the change made and the purpose for the change.

Integrating third-party software

During project development there may be some requirement to interface to third-party software components, especially within an embedded environment. It is highly unlikely that such externally written code will adhere to the protocols and standards outlined within this standards document. Therefore it is sometimes impossible to ensure that our interfacing code is written to meet the standards we set ourselves internally. The imperative in such situations is that any code written to comply to alien standards should have comprehensive documentation, either coherent in-code remarks or a supplementary document, archived alongside the project.

Naming anomalies

The naming format of function names, variable types, and enumerate definitions within this coding convention are strictly defined, and goes some way to help identify an object’s nature, scope and location from the name content. Third-party objects may have names that completely contradict our conventions, and may even prove to ‘confuse’ our code. Such interfacing code must be commented sufficiently to avoid any confusion on subsequent review.

A possible solution may be to have an intermediary module, which acts as ‘wrapper functions’ between the third party code and the rest of the project(s). This can be used to create aliases for the third party interface, thus ensuring that all calling code still conforms to the coding convention. The development of such a module is entirely at the developer’s discretion, and would have to be thoroughly commented to assist all developers undertaking future integration.

Casting of variables to third party functions

There may be a need to explicitly cast variable parameters being passed to third-party functions, possibly referring to the standard C data types in the process. This is prohibited in code calling native functions, but may be performed with third-party functions to avoid compilation warnings.

Appendix A – Glossary of terms

Term Description
Module Another term for a file or a group of files implementing a certain functionality). Several modules may be combined together in a project to form a library
Hard tab A single ASCII character (code 9) used in word processors to denote a horizontal tabulation. The exact number of white spaces that this is used to represent is not defined, and is configurable by most applications. For this reason hard tabs are avoided in C source development.
Soft tab Multiple white spaces used in word processors to denote a horizontal tabulation. Because of the substitution of multiple characters, this method results in larger files, but more importantly, the code content is presented identically in all applications. This is the preferred tabulation method in C source development.
Local A definition scope whereby the object can only be referenced from other code within the same module. Such objects are defined using the static keyword.
Global A definition scope whereby the object can be referenced from any code within any module in the project. Such objects are defined using without using the static keyword.
Interface Used to describe a suite of global functions (and their parameter definitions) that allows access to the parameters and functionality of a module. This interface is supplied by an include file.

Appendix B - File comment headers

The are headers that should be adopted when developing.

C source file

/*******************************************************************************
 * Copyright © 2014 The DTVKit Open Software Foundation Ltd (www.dtvkit.org)
 * Copyright © 2004 Ocean Blue Software Ltd
 *
 * This file is part of a DTVKit Software Component
 * You are permitted to copy, modify or distribute this file subject to the terms
 * of the DTVKit 1.0 Licence which can be found in licence.txt or at www.dtvkit.org
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
 * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * If you or your organisation is not a member of DTVKit then you have access
 * to this source code outside of the terms of the licence agreement
 * and you are expected to delete this and any associated files immediately.
 * Further information on DTVKit, membership and terms can be found at www.dtvkit.org
 *******************************************************************************/
/**
 * @brief   File functionality
 * @file    filename
 * @date    Date
 */

#ifndef _MYFILE_H

#define _MYFILE_H

/*---Constant and macro definitions for public use-----------------------------*/

/*---Enumerations for public use-----------------------------------------------*/

/*---Global type defs for public use-------------------------------------------*/

/*---Global Function prototypes for public use---------------------------------*/

#endif /*  _MYFILE_H */

/******************************************************************************
** End of file
******************************************************************************/

H Header file

/*******************************************************************************
 * Copyright © 2014 The DTVKit Open Software Foundation Ltd (www.dtvkit.org)
 * Copyright © 2004 Ocean Blue Software Ltd
 *
 * This file is part of a DTVKit Software Component
 * You are permitted to copy, modify or distribute this file subject to the terms
 * of the DTVKit 1.0 Licence which can be found in licence.txt or at www.dtvkit.org
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
 * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * If you or your organisation is not a member of DTVKit then you have access
 * to this source code outside of the terms of the licence agreement
 * and you are expected to delete this and any associated files immediately.
 * Further information on DTVKit, membership and terms can be found at www.dtvkit.org
 *******************************************************************************/
/**
 * @brief   File functionality
 * @file    filename.h
 * @date    Date
 */

/*---includes for this file--------------------------------------------------*/
/* compiler library header files */
#include <stdio.h>
/* third party header files */

/* OBS header files */
#include <techtype.h>
#include <dbgfuncs.h>

/*---constant definitions for this file--------------------------------------*/

/*---local typedef structs for this file-------------------------------------*/

/*---local (static) variable declarations for this file----------------------*/
/*   (internal variables declared static to make them local)

/*---local function prototypes for this file---------------------------------*/
/*   (internal functions declared static to make them local)

/*---local function definitions----------------------------------------------*/

/*---global function definitions---------------------------------------------*/


/****************************************************************************
** End of file
*****************************************************************************/

Function comment Header

/**
 * @brief    Function description.
 * @param    In and Out Parameters. (type and meaning)
 * @return   Return data
 * @warning  Any known warnings
 * @bug      Any Known bugs
 */


coding_standards.txt · Last modified: 2020/11/18 15:59 (external edit)