this page is a revisited mirror under construction (!)

Coding Standards for C, C++, and Java

Vision 2000 CCS Package and Application Team

http://v2ma09.hst.nasa.gov/coding_standards.html


Introduction

The purpose of these coding standards is to facilitate the maintenance, portability, and reuse of custom C, C++, and Java source code developed for the Vision 2000 CCS. These standards were developed from a variety of sources, including other standards documents, software examples from defacto standard language references, and personal experience. As with any guidelines, there will be circumstances where full compliance is not desirable for efficiency, maintainability, or other reasons. In these cases, conformance should not be pursued simply for the sake of meeting the standards.

This document is organized into four sections:

The symbol is used to denote language specific information. Within tables, "n/a" denotes "not applicable".


1 File Organization

1.0 File Contents

Files should be used to organize related code modules, either at the class (for C++ and Java) or function (for C) level. The following table identifies the contents of individual files for each language:

File contents C C++ Java
class declaration (header) n/a X n/a
class definition (source) n/a X X
main function X X (with primary class)
function(s) X X n/a
globals X X n/a

1.1 Source File Layout

Source files should contain the following components in the order shown:

File contents C C++ Java
prolog X X X
package imports n/a n/a X
system #includes X X n/a
application #includes X X n/a
external functions X X n/a
external variables X X n/a
constants X X X
static variable initializations X X X
class declaration n/a n/a X
public methods n/a X X
protected methods n/a X X
private methods n/a X X
functions X X n/a

1.2 Header File Layout

Header files should contain the following components in the order shown (note that Java does not use header files):

File contents C C++ Java
file guard X X n/a
prolog X X n/a
system #includes X X n/a
application #includes X X n/a
#defines X X n/a
macros X X n/a
external functions X X n/a
external variables X X n/a
constants X X n/a
structs X X n/a
forward declarations X X n/a
class declaration n/a X n/a
public methods n/a X n/a
protected methods n/a X n/a
private methods n/a X n/a
inline method definitions n/a X n/a
functions X X n/a

1.3 Header File Guard

(C,C++) All header files should contain a file guard mechanism to prevent multiple inclusion. This mechanism is implemented as shown by the following lines:
#ifndef MeaningfulNameH		// first line of the header file
#define MeaningfulNameH		// second line of the header file
	.
	.			// body of the header file
	.
#endif  // MeaningfulNameH	// last line of the header file; note comment

2 Naming Conventions

The following table summarizes the naming conventions:

Identifier C C++ Java
package n/a shortname
class, union, struct MeaningfulName
exception class n/a MeaningfulException
interface n/a MeaningfulActionable
typedef MeaningfulName n/a
enum n/a MeaningfulName n/a
function, method meaningfulName
accessor method n/a getX, setX
object, variable meaningfulName
#define, macro MEANINGFUL_NAME n/a
const, static final variable MEANINGFUL_NAME
source file .c .cpp .java
header file .h n/a

2.1 Descriptive Names

Names should be readable and self-documenting. Abbreviations and contractions are discouraged. Shorter synonyms are allowed when they follow common usage within the domain.

2.2 Valid Characters

All names should begin with a letter. Individual words in compound names are differentiated by capitalizing the first letter of each word as opposed to separating with an underscore. The use of special characters (anything other than letters and digits), including underscores is strongly discouraged. The first 31 characters of a name (including the prefix) must be unique, due to restrictions of various platforms. The uniqueness must not be due solely to a difference in case.

2.3 File Names

Filenames should only contain one period, to separate the file extension.

2.4 Function Names

Function names should preferably be an action verb. Boolean-valued functions (those that have two possible return values) should use the "is" prefix as in "isEmpty()".

(C, C++) All functions must be prototyped, with the prototypes residing in header files.

2.5 Namespaces

Namespace collision should be minimized without introducing cryptic naming conventions by using the C++ namespace or Java package constructs.

(Java) Create a new Java package to group classes of related functionality. Package source and class files then reside in a convenient hierarchical directory structure that maps directly to the package name.


3 Style Guidelines

The primary purpose of style guidelines is to facilitate long-term maintenance. During maintenance, programmers who are usually not the original authors are responsible for understanding source code from a variety of applications. Having a common presentation format reduces confusion and speeds comprehension. Therefore, the following guidelines are specified based on the principles of good programming practice and readability. In the cases where two or more equally valid alternatives are available, one was selected to simplify specification. In the future, automated tools may be used to apply style guidelines to source code files.

3.1 Lines

3.1.1 Line Length

All lines should be displayable without wrapping on an 80-character display. If wrapping is required, try to break at an operator, and start the next line with the operator vertically aligned. For example:
    cout << "This is an example of a line which must be wrapped, value = "
         << value << endl;

3.1.2 Statements Per Line

Each statement should begin on a new line.

3.2 Comments

3.2.1 Automated Documentation Comments

For comments meant to be extracted by an automated documentation tool, follow the Java convention of using the standard C comment delimiters with an extra asterisk on the first one, as shown:
    /**
      * This is a module, class, function, or instance variable comment
      * that will be extracted by an automated documentation tool.
      */
This will provide a consistent look across all source code files, and should facilitate creation of automated documentation tools.

Such comments should be used to describe classes, methods, and global or instance variables.

3.2.2 Code Block Comments

Code block comments should precede the block, be at the same indentation level, and be separated by a blank line above and below the comment. Brief comments regarding individual statements may appear at the end of the same line, and should be vertically aligned with other comments in the vicinity for readability.

(C) Code block comments should use the standard C comment delimiters /* and */.
(C++, Java) Code block comments should use the single line comment delimiter //.

3.2.3 Blank Lines

Use a single blank line to separate logical groups of code to improve readability. In source files, use two blank lines to separate each function.

3.3 Formatting

3.3.1 Spacing Around Operators

Spacing around operators and delimiters should be consistent. In general, insert one space before or after each operator to improve readability. Use spaces inside of the parentheses around the argument list. Do not use a space within empty argument lists () or non-dimensioned arrays [].

(C++) Do not use spaces around the scope operator ::.
(C++, Java) Do not use spaces around the member access operators . and ->.
	if ( value == 0 ) {		// right	
	if (value==0){			// not recommended

	void doIt( int v );		// right
	void doIt(int v);		// not recommended

	value = object->GetValue();	// right
	value=object -> GetValue();	// wrong

3.3.2 Indentation and Braces

The contents of all code blocks should be indented to improve readability. A single tab or four spaces are recommended as the standard indentation. Braces should be placed to show the level of indentation of the code block, with the open brace at the end of the statement which starts the block, and the close brace indented to match the statement.
	int main() {
		doSomething();
	}

	struct MyStruct {
		int x;
		int y;
	}

	if ( value == 0 ) {
		doSomething();
	} else if ( value == 2 ) {   // note position of cascaded if statement
		doSomething2();
	} else {
		doSomething3();
	}

	while ( value < 300 ) {
		doSomething();
	}

	do {
		doSomething();
	} while ( value < 300 )	     // note: ending brace and control on same line

	switch ( value ) {
	case 1:
		doSomething();
		break;
	case 2:
	case 3:
		doSomething2();
		break;
	default:
		break;		     // final break required
	}

3.3.3 Pointer and Reference Position

(C, C++) All declarations of pointer or reference variables and function arguments should have the dereference operator * and the address-of operator & placed adjacent to the type, not the variable. For example:
	char*	text;			// right
	char 	*text;			// not recommended

	char*	doSomething( int* x );	// right
	char	*doSomething( int *x );	// not recommended

3.4 Statements

3.4.1 Control Statements

All control statements should be followed by an indented code block enclosed with braces, even if it only contains one statement. This makes the code consistent and allows the block to be easily expanded in the future. For example:
	if ( value == 0 ) {		    // right
		doSomething();
	}

	if ( value == 0 ) doSomething();    // wrong - no block, not indented

	if (value == 0)
		doSomething();		    // wrong - no block

3.4.2 Conditional Statements

Conditional statements found in if, while, and do statements should be explicit based on the data type of the variable being tested. For example:
	int value = getValue();
	if ( value == 0 ) {		    // right
		doSomething();
	}
	if ( !value ) {			    // wrong - not explicit test
		doSomethingElse();
	}
(Java)
	boolean value = getValue();
	if ( !value ) {			    // right - this is explicit
		doSomethingElse();	    // test for boolean type
	}

3.4.3 Include Statements

(C,C++) For both source and header files, #include statements should be grouped together at the top of the file after the prolog. Includes should be logically grouped together, with the groups separated by a blank line. System includes should use the <file.h> notation, and all other includes should use the "file.h" notation. Path names should never be explicitly used in #include statements (with the exception of vendor library files such as Motif), since this is inherently non-portable. For example:
	#include <stdlib.h>			// right
	#include <stdio.h>			//			
	#include <Xm/Xm.h>			//
	#include "meaningfulname.h"		//

	#include "/proj/util/MeaningfulName.h"	// wrong - explicit path,
	#include <stdlib.h>			//  out of order,
	#include </usr/include/stdio.h>		//  path for system file,
	#include "Xm/Xm.h"	 		//  local include of library file 

3.5 Declarations

3.5.1 Variable Declaration

Each variable should be individually declared on a separate line. Variables may be grouped by type, with groups separated by a blank line. Variable names should be aligned vertically for readability. There is no required ordering of types, however some platforms will give optimal performance if declarations are ordered from largest to smallest (e.g., double, int, short, char).
	int*	a;		// right
	int	b;		//
	int	c;		//
	double	d;		//
	double	e;		//

	double	a;		// right 
	int	b;		//

	double	d;		// acceptable - not grouped by type
	int	b;		//
	int*	a;		//
	double	e;		//
	int	c;		//
	
	int*	a, b, c;	// wrong - not individually declared, not
				// on separate lines

	int*	a,		// wrong - not individually declared
		b,		//   	
		c;		//  
The two preceding examples are prone to error; notice that a is declared as a pointer to integer and b and c are declared as integers, not as pointers to integers.

3.5.2 External Variable Declaration

(C,C++) All external variables should be placed in header files. In general the use of global variables is discouraged. Use the following method to allow external variables to be created only once while using a single declaration. In the header file which declares the global variable, use a flag to cause the default action on inclusion to be referencing of an externally created variable. Only in the source file that wants to actually create the variable will this flag be defined.

In the header file MeaningfulName.h,

	#ifdef MeaningfulNameInit     // the flag is called MeaningfulNameInit
	#define EXTERN		      // create the variable (only in main.cpp)
	#else
	#define EXTERN extern	      // just a reference (default) 
	#endif
	EXTERN ErrorLogger errorLog;
	#undef EXTERN
All of the source files should include this header file normally:
	#include meaningfulname.h	
while the following should appear only in the source file where you actually want to declare the variable and allocate memory for it (typically in main.cpp):
	#define MeaningfulNameInit
	#include meaningfulname.h
	#undef MeaningfulNameInit

3.5.3 Numeric Constant Declaration

Use only the uppercase suffixes (e.g., L, X, U, E, F) when defining numeric constants. For example:
	const int    value  = A73B2X;	// right, hexadecimal constant
	const double evalue = 1.2E9;	// right, scientific notation constant

	const float  fvalue = 1.2e9;	// wrong, lowercase e

3.5.4 Enumerated Type Declaration

(C++) The enum type name and enumerated constants should each reside on a separate line. Constants and comments should be aligned vertically. Following is an example of a valid enum declaration:
	enum CompassPoints {	// Enums used to specify direction.
		North = 0,	// 
		South = 1,	// 
		East  = 2,	// 
		West  = 3	//
	};

3.5.5 Struct and Union Declaration

(C,C++) The struct type name and structure members should each reside on a separate line. This format separates the members for easy reading, is easy to comment, and eliminates line wrapping for large numbers of members. Each struct should have a one-line description on the same line as the type name. Each member should have a comment describing what it is, and units if applicable. Members and comments should be aligned vertically. Following is an example of a valid struct declaration:
	struct MeaningfulName {		// This is a struct of some data.
		int	firstInteger;	// This is the first int.
		int	secondInteger;	// This is the second int.
		double	firstDouble;	// This is the first double.
		double	secondDouble;	// This is the second double.
	};

3.5.6 Class Declaration

(C++,Java) All class definitions should include a constructor and a destructor. Providing a deep copy constructor (i.e., allocates memory and copies the object, not just maintains a pointer to the other object) is strongly recommended. All classes should have public, protected, and private access sections declared, in this order. Friend declarations should appear before the public section. All member variables should be either protected or private. It is recommended that definitions of inline functions follow the class declaration, although trivial inline functions (e.g., {} or { return x; }) may be defined within the declaration itself. Each member function and variable should be commented using the automated documentation comment delimiter /**. Member functions should be commented in the same fashion as a regular function. Member variables should each have a one line description. Members and comments should be aligned vertically. For example:
	class Value : public BaseValue {
	public:
		Value();			// Default constructor.
		Value( const Value& oldValue );	// Copy constructor.
		~Value();			// Destructor.
		void setValue( int newValue );	// Set value.
		int getValue();			// Get value.
 
	protected:
		void incrementValue();		// Increment value.

	private:
		int value;			// The value.
	};

4 Recommended Programming Practices

4.1 Placement of Declarations

Local variables can be declared at the start of the function, at the start of a conditional block, or at the point of first use. However, declaring within a conditional block or at the point of first use may yield a performance advantage, since memory allocation, constructors, or class loading will not be performed at all if those statements are not reached.

4.2 Switch Statements

Specify a break statement after every case block, including the last one. It is recommended that a default case always be defined.

4.3 Return Statements

Where practical, have only one return from a function or method as the last statement. Otherwise, minimize the number of returns. Highlight returns with comments and/or blank lines to keep them from being lost in other code.

4.4 Casts

Avoid the use of casts except where unavoidable, since this can introduce run-time bugs by defeating compiler type-checking. Working with third-party libraries (e.g., X or Motif) often requires the use of casts.

4.5 Literals

Use constants instead of literal values wherever possible. For example:
const double PI = 3.141259;			// right
const char APP_NAME = "ACME Spreadsheet 1.0";	// right

area = 3.141259 * radius * radius;		// not recommended 
cout << "ACME Spreadsheet 1.0" << endl;		// not recommended

4.6 Explicit Initialization

In general, explicitly initialize all variables before use.

4.7 Constructs to Avoid

(C,C++)

The use of #define constants is strongly discouraged, using const is recommended instead.

The use of #define macros is strongly discouraged, using inline functions is recommended instead.

The use of typedef is strongly discouraged where actual types such as class, struct, or enum could be used instead.

The use of extern (e.g., global) variables is strongly discouraged. The exception is for programs which benefit from having a small number of object pointers accessible globally via extern. The use of goto statements is not allowed.

4.8 Macros

(C, C++) All arguments to macros should be enclosed in parentheses to eliminate ambiguity on expansion. For example:
#define MAX( x, y )   ( (x) > (y) ) ? (x) : (y) )

4.9 Debug Compile-time Switch

(C,C++) Code used only during development for debugging or performance monitoring should be conditionally compiled using #ifdef compile-time switches. The symbols to use are DEBUG and STATS, respectively. Debug statements announcing entry into a function or member function should provide the entire function name including the class. For example:
#ifdef DEBUG
    cout << "MeaningfulName::doSomething: about to do something" << endl;
#endif

4.10 Memory Management

(C++) Use new and delete instead of malloc/calloc/realloc and free. Allocate memory with new only when necessary for variable to remain after leaving the current scope. Use the delete [] operator to deallocate arrays (the use of delete without the array operator to delete arrays is undefined). After deletion, set the pointer to zero, to safeguard possible future calls to delete. C++ guarantees that delete 0 will be harmless.

4.11 Constructors

(C++) All constructors should initialize all member variables to a known state. This implies that all classes should have a default constructor (i.e., MyClass();) defined. Providing a deep copy constructor is strongly recommended.

4.12 Destructors

(C++) All classes which allocate resources which are not automatically freed (e.g., have pointer variables) should have a destructor which explicitly frees the resources. Since any class may someday be used as a base class, destructors should be declared virtual, even if empty.

4.13 Argument Passing

(C++) If the argument is small and will not be modified, use the default pass by value. If the argument is large and will not be modified, pass by const reference. If the argument will be modified, pass by reference. For example:
void A::function( int notChanged );		  // default: pass by value

void B::function( const C& bigReadOnlyObject )    // pass by const reference

void C::function( int notChanged, int& result );  // pass by reference

4.14 Default Arguments

(C++) Where possible, use default arguments instead of function overloading to reduce code duplication and complexity.

4.15 Overriding Virtual Functions

(C++) When overriding virtual functions in a new subclass, explicitly declare the functions virtual. Although not required by the compiler, this aids maintainability by making clear that the function is virtual without having to refer to the base class header file.

4.16 Const Member Functions

(C++) It is recommended that all member functions which do not modify the member variables of an object be declared const. This allows these functions to be called for objects which were either declared as const or passed as const arguments.

4.17 Referencing Non-C++ Functions

(C++) Use the extern "C" mechanism to allow access to non-C++ (not just C) functions. This mechanism disables C++ name mangling, which allows the linker to resolve the function references. For example:
extern "C" {
    void aFunction();           // single non-C++ function prototype 
}

extern "C" {
#include "functions.h"          // library of non-C++ functions 
}

4.18 NULL Pointer

(C++) Use the number zero (0) instead of the NULL macro for initialization, assignment, and comparison of pointers. The use of NULL is not portable, since different environments may define it to be something other than zero (e.g., (char*)0).

4.19 Enumerated Types

(C++) Use enumerated types instead of numeric codes. Enumerations improve robustness by allowing the compiler to perform type-checking, and are more readable and maintainable.

4.20 Terminating Stream Output

(C++) Use the iostream manipulator endl to terminate an output line, instead of the newline character \n. In addition to being more readable, the endl manipulator not only inserts a newline character but also flushes the output buffer.

4.21 Object Instantiation

(C++,Java) Where possible, move object declarations and instantiations out of loops, using assignment to change the state of the object at each iteration. This minimizes overhead due to memory allocation from the heap.

4.22 Encapsulation

(C++,Java) Instance variables of a class should not be declared public. Open access to internal variables exposes structure and does not allow methods to assume values are valid.

4.23 Default Constructor

(Java) Where possible, define a default constructor (with no arguments) so that objects can be created dynamically using Class.newInstance(). This exploits the power of Java to dynamically link in functionality that was not present at compile time.

4.24 Importing Packages

(Java) Use full package names instead of wildcards when importing to improve comprehensibility and provide context.


Additional Coding Standards and Style Guides

For additional background and suggestions, there are a number of coding standard documents available on the Web:


Vision Home Page CCS GUI

Last update November 1, 1996 by Jeff Johnson jjohnson@v2pop.hst.nasa.gov