http://wiki.sipfoundry.org/display/sipXecs/Coding+Standards
sipX Project Coding Standards for C, C++, and Java
Actually, a language is characterized not so much by what it lets us program as by what it keeps us from expressing. As E.W. Dijkstra observed, the programmer's most difficult, daily task is to not mess things up. The first and most noble duty of a language is thus to help in this eternal struggle. -- Niklaus Wirth
Last Modified 2005-07-25 15:05:12-0400
NOTE: These coding standards are derived from the coding standards developed for the Vision 2000 CCS project, Todd Hoff's C++ coding standards, and AmbySoft's Java coding standards.
A good developer knows that there is more to development than programming.
A great developer knows that there is more to development than development.
Remember that you write code for three audiences: The computer, for whom it must be correct, your muse, for which it it must be elegant, and the poor schmuck who has to modify it, for whom it must be comprehensible.
-- Daniel R. Killoran
Introduction
The purpose of these coding standards is to facilitate the maintenance, portability, and reuse of custom C, C++, and Java source code developed by the sipX open source project. 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.
0.1 When the standards are not followed, it is advisable to add a comment with the reason for non-conformance.
-
1 File Organization
- Source file contents and layout. 2 Naming Conventions
- How to name files and identifiers. 3 Style Guidelines
- Guidelines for how source code should look. 4 Recommended
-
- 4.1 Placement of Declarations
- 4.2 Switch Statements
- 4.3 Return Statements
- 4.4 Casts
- 4.5 Literals
- 4.6 Explicit Initialization
- 4.7 Checking Error Returns
- 4.8 Short Methods
- 4.9 Document Null Statements
- 4.10 Comments on Comments
- 4.11 Constructs to Avoid
- 4.12 Macros
- 4.13 Debug Compile-time Switch
- 4.14 Memory Management
- 4.15 Constructors
- 4.16 Destructors
- 4.17 Argument Passing
- 4.18 Default Arguments
- 4.19 Overriding Virtual Functions
- 4.20 Const Member Functions
- 4.21 Referencing Non-C++ Functions
- 4.22 NULL Pointer
- 4.23 Enumerated Types
- 4.24 Terminating Stream Output
- 4.25 Object Instantiation
- 4.26 Encapsulation
- 4.27 Default Constructor
- 4.28 Importing Packages
- 4.29 Exception Handling
5 Source Code Templates
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. When creating new source files, developers should use the appropriate template file as a starting point.
File contents | C | C++ | Java |
---|---|---|---|
prolog (incuding copyright) | 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 |
creators | n/a | X | X |
manipulators | n/a | X | X |
accessors | n/a | X | X |
inquiry | n/a | X | X |
protected methods | n/a | X | X |
private methods | n/a | X | X |
non-testing | n/a | X | X |
testing | n/a | X | X |
functions | X | X | n/a |
- (C, C++) When it is possible to put a needed #include line in the source file instead of in the header file, do so. This will reduce unnecessary file dependencies and save a little compile time.
1.2 Header File Layout
Header files should contain the following components in the order shown (note that Java does not use header files). When creating new header files, developers should use the appropriate template file as a starting point.
File contents | C | C++ | Java |
---|---|---|---|
prolog (including copyright) | X | X | n/a |
file guard | 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 |
creators | n/a | X | X |
manipulators | n/a | X | X |
accessors | n/a | X | X |
inquiry | n/a | X | X |
protected methods | n/a | X | n/a |
private methods | n/a | X | n/a |
non-testing | n/a | X | X |
testing | n/a | X | X |
inline method definitions | n/a | X | n/a |
functions | X | X | n/a |
- (C++) Small inline methods may be implemented in the class definition.
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 _MeaningfulName_h_ // first line of the header file following the prolog #define _MeaningfulName_h_ // second line of the header file following the prolog . . // body of the header file . #endif // _MeaningfulName_h_ // last line of the header file; note comment
1.4 Doxygen Documentation
C++ (and C) code uses the Doxygen system for generating documentation from header and source files. Doxygen supports a wide range of styles; this section provides recommendations for how it should be used in sipX projects.
1.4.1 Comment Style
Doxygen comments are distinguished from normal comments by an extra comment character at the beginning, and are associated with the adjacent code by their placement. Both C style comments and C++ single-line style comments are supported.
-
C multiple-line style comments
-
Doxygen interprets any comment that has an extra '*' character at the beginning of the comment:
/** Doxygen single line comment */ /** * Doxygen comment * that extends over more than one line * this form should be used whenever there is more than one line */
C++ single-line style comments
-
Doxygen interprets any C++ comment that has an extra '/' character at the beginning of the comment:
/// Doxygen single line comment
Multiline comments composed of multiple C++ style comments are not preferred:/// Doxygen comment /// that extends over more than one line
1.4.2 Comment Placement
Doxygen comments are, by default, associated with the code that immediately follows the comment:
/// indicates whether or not the object currently allows any Foo actions. void isFooAllowed();
For some constructs it is more readable to place the comment after the code element it documents, which is accomplished by adding a '<' character after the Doxygen comment indicator. This may be used with either single or multi-line comments:
void doFooAt( int offset, ///< the offset into the Foo char* name /**< * the name to look up for this Foo action * in the FooMgr database. */ );
Placing the Doxygen comment after the element it documents in this way is preferred whenever the element is a member of a list, as in parameter declarations or enum values.
1.4.3 Class Documentation
A class declaration must include a detailed description comment preceeding the class:
/** * FooFactory is a factory class that constructs instances of the * subclasses of Foo based on information obtained from the * foo-config file. */ class FooFactory {
The class comment is a good place to put general guidelines about how the methods in the class relate to one another.
1.4.4 Member Grouping
By default, Doxygen groups the members within a class based on heuristics that use the public/protected/private scope and the method signatures. For simple classes this usually works well, and may be used. When a class has many methods, it is usually better to explicitly control how they are grouped in the documentation, and to provide additional documentation at the group level. To explicitly control grouping, add the 'nosubgrouping' Doxygen command to the class comment:
/**
* FooFactory is a factory class that constructs instances of the
* subclasses of Foo based on information obtained from the
* foo-config file.
*
* @nosubgrouping
*/
class FooFactory
{
Each group is then formed of the following elements:
- an introducing Doxygen comment that supplies the group name using the 'name' Doxygen command,
- the detailed comment for the group,
- the Doxygen group start command '{',
- the declarations of the members in the group and accompanying documentation, and finally
- the Doxygen group end command '}'.
For example (preceeded here by a non-Doxygen comment with a line of '=' characters so that it reads better in the source file):
// ================================================================ /** @name Searching * * The searching methods apply a compiled regular expression to a subject * string. All searching methods return a boolean result indicating whether * or not some match was found in the subject. To get information about * the match, use the Results methods. */ ///@{ ... the methods in the group ... ///@}
1.4.5 Member Function Documentation
Each member function should have:
- a brief single line description preceeding the member declaration,
- parameter descriptions following each parameter, and
- a more detailed description following the declaration, which should include a Doxygen 'returns' command if the method returns a value.
/// Search a string for matches to this regular expression. bool Search( const char * subject, ///< the string to be searched for a match int len = -1, ///< the length of the subject string int options = 0 ///< sum of any PCRE options flags ); /**< * Apply the regular expression to the subject string. * Optional parameter len can be used to pass the subject's length to * Search(). If not specified (or less than 0), strlen() is used * internally to determine the length. Parameter options can contain * any combination of options; for options documentation, see 'man pcre' * @returns true if a match is found. */
1.4.6 Examples
Including examples in the documentation is strongly encouraged. To embed an example, use the Doxygen "@code ... @endcode" construct:
/**< * May only be called after a successful search * and applies to the results of that call. * @returns true if there was an ith match, false if not * * Example:@code * RegEx matchBs("((B)B+)"); * UtlString getB; * UtlString getBs; * if (matchB.Search("xxaBBBBcyy")) * { * matchB.MatchString(&getBs,0); * matchB.MatchString(&getB,2); * } * @endcode * would set the UtlStrings * - getBs to "BBBB" * - getB to "B" */
1.4.7 Lists
Numbered and bulleted lists are supported in Doxygen comments using simple indentation conventions:
/**< * A numbered list is created using '#' characters: * # first numbered item * # second numbered item * # first item in nested numbered list (2.1) * * A bullet list uses '-' characters: * - first bullet * - second bullet * - nested bullet */
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, *MeaningfulNamePtr | n/a | |
enum | n/a | MeaningfulName | n/a |
function, method | PseudoClass_MeaningfulName | meaningfulName | |
accessor method | n/a | getX, setX | |
inquiry method | isX, hasX | ||
object, variable, argument | meaningfulName | ||
class attribute | n/a | mMeaningfulName | |
pointer variables | pMeaningfulName | n/a | |
reference variable | n/a | rMeaningfulName | n/a |
global variable | gMeaningfulName | n/a | |
static variable | sMeaningfulName | ||
#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. When confronted with a situation where you could use an all uppercase abbreviation instead use an initial upper case letter followed by all lower case letters. 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 Prefixes and Suffixes
Prefixes are sometimes useful:
-
Is, Has - to ask a question about something.
- Whenever someone sees Is or Has they will know it's a question Get - get a value Set - set a value
Suffixes are sometimes useful:
-
Max - to mean the maximum value something can have
Cnt - the current count of a running count variable
Key - key value
2.4 Class Attribute Names
-
(C++, Java) All attribute names should be prepended with the character 'm' in order to avoid any conflict with method names.
(C++) The 'm' prefix always precedes other name modifiers like 'p' for pointer or 'r' for reference.
2.5 File Names
Filenames should only contain one period, to separate the file extension.
2.6 Function Names
Function and method names should preferably begin with 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.7 Namespaces
- (C, C++) Libraries in danger of conflicting with functions, classes or methods defined in other libraries should choose some sort of unique prefix to avoid name conflicts.
- (C++) The C++ namespace construct should not be used. This construct is not yet widely supported.
- (Java) Namespace collision should be minimized without introducing cryptic naming conventions by using the Java package construct. 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.
2.8 Subversion Properties
2.8.1 svn:ignore
A file that is generated in the course of building the software should be listed in its containing directory's svn:ignore
property. This prevents it from being displayed by 'svn status'. All files that 'svn status' reports as unknown (flags with '?') should be files that the user/developer created manually.
More information on svn:ignore
is in the Subversion book at http://svnbook.red-bean.com/en/1.1/ch07s02.html#svn-ch-7-sect-2.3.3
.
2.8.2svn:mime-type
and svn:eol-style
In order to ensure that files checked out from the repository have the correct end-of-line characters for the platform onto which they are checked out, every file in the respository must have the appropriate values for the Subversion properties svn:mime-type
and/or svn:eol-style
.
All "binary" files in the repository should have a valid, non-text MIME media type specified for their svn:mime-type
property. (If no other type is suitable, "application/octet-stream" can be used.)
All text files must have a "text/*" svn:mime-type
property, and an svn:eol-style
property. Most text files are relevant to all platforms, and should have the value "native" for the svn:eol-style
property. This ensures that a Subversion checkout will use the right end-of-line characters for the host operating system.
Text files that are specifically for use only on MS-Windows and similar OS's should have an svn:eol-style
property of "CRLF", so that they can be checked out on other OS's and made visible to MS-Windows systems (through shared disks) without worrying about end-of-line translation.
Setting "automatic properties" in your Subversion configuration can automate setting these properties in most cases. A sample Subversion configuration file ($HOME/.subversion/config
or Application Data\Subversion\config
) that works for most of the file extensions used in sipX can be retrieved here. The list of officially-defined MIME media types is at http://www.iana.org/assignments/media-types
. SipX also uses WAV audio files, which have no officially-defined MIME media type, but do have a commonly understood extension type, audio/x-wav. More information on svn:mime-type
is at http://svnbook.red-bean.com/en/1.1/ch07s02.html#svn-ch-7-sect-2.3.2
. More information on svn:eol-style
is at http://svnbook.red-bean.com/en/1.1/ch07s02.html#svn-ch-7-sect-2.3.5
. More information on configuring Subversion is at http://scm.sipfoundry.org/svndoc/ch07.html#svn-ch-7-sect-1
.
There is a script in the developers' tool directory that will check that a working copy is following these rules. Commonly it is run from the top directory of a working copy via: sipXpbx/doc/developer/eol-check.sh sipX*
2.8.3 sf:tab
Most text files in sipX projects should not contain tab characters; they are allowed only when the file type specifically requires them and no alternative is available; the primary example of this is Makefiles.
Files which are exceptions to this rule can be marked by setting the sf:tab
Subversion property. The value 'initial' can be set for files for which tabs are allowed at the beginnings of lines. The principal use for this value is "makefiles". For this reason, sf:tab
= initial allows not only tabs at the beginnings of lines, but also tabs preceded by '#' (for commenting out lines).
The tab-check.sh script checks that all files with a text MIME-type do not contain tab characters.
The value sf:tab
= yes allows tabs in any location in a file. This value should only be used on files imported from external sources and files which require the use of tabs (e.g., some Windows build control files).
2.9 strcasecmp
and strncasecmp
The case-insensitive string comparison functions strcasecmp
and strncasecmp
were added to Posix relatively recently. Many older Windows development environments did not provide these functions, but rather provided functions named stricmp
and strnicmp
which provided the same functionality with the same interface. To avoid making each source file that needs these functions test its environment, the include file os/OsDefs.h
provides #define's to convert strcasecmp
and strncasecmp
into stricmp
and strnicmp
when necessary.
Any file which needs to use this functionality must use the names strcasecmp
and strncasecmp
and must #include os/OsDefs.h
to ensure they are mapped if the environment requires it. (Certain files obtained from third-party sources are exceptions to this rule.)
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
Lines should not exceed 98 characters in order to display without wrapping on an 100-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.1.3 Final EOL
In all text files, the final line should end with the appropriate end-of-line characters (for the host operating system). (Zero-length text files are acceptable, as they contain no lines.)
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.
- Do not use spaces inside of the parentheses around the argument list.
- Do not use a space within empty argument lists () or non-dimensioned arrays [].
- Do not use spaces around the member access operators . and ->.
- (C++) Do not use spaces around the scope operator ::.
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. Indent using 3 (recommended), 4, or 8 spaces for each level. Do not use tabs, use spaces. Most editors can substitute spaces for tabs. See also the sf:tab
Subversion property.
Of the three major brace styles, two are acceptable - with the first one listed being preferable. For existing files, be consistent with the brace style already in use in the file - Do no mix styles within a file.
- Place brace under and inline with keywords:
if (condition) if (condition) while (condition) int main() { { { { . . . . . . . . . . . . } } } } else if (condition) { . . . } else { . . . } struct MyStruct { int x; int y; }
- Traditional Unix policy of placing the initial brace on the same line as the keyword and the trailing brace on its own line and inline with the keyword. An exception is made for method and function definitions where the initial brace is handled as in the first style.
if (condition) { if (condition) { while (condition) { int main() . . . . . . . . . { } } } . . . else if (condition) { } . . . } else { . . . } struct MyStruct { int x; int y; }
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:
(C,C++)
int value = getValue(); if (value == 0) // right { doSomething(); }
if (!value) // wrong - not explicit test { doSomethingElse(); }
bool value = getValue(); // could be RWBoolean too. if (!value) // right { 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 "meaningfulmame.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. The following is an example of a valid enum declaration:
enum CompassPoints { // Enums used to specify direction. North = 0, // South = 1, // East = 2, // West = 3 // };
- (C++) It's often useful to be able to say an enum is not in any of its valid states. Make a label for an unitialized or error state. Make it the first label if possible.
enum States { STATE_ERR, STATE_OPEN, STATE_RUNNING, STATE_DYING };
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 default constructor, (virtual) destructor, copy constructor and operator=. If the class has a pointer, provide a deep copy constructor (i.e., allocates memory and copies the object being pointed to, not just maintains a pointer to the original object). If any of these four are not currently needed, create stub versions and place in the private section so they will not be automatically generated, then accidentally used. (This protects from core dumps.) 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 unless multiple labels are used for one selection of code. 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. Possibly highlight returns with comments and/or blank lines to keep them from being lost in other code. Multiple returns are generally not needed except for reducing complexity for error conditions or other exceptional conditions.
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. When you need to cast, document the reasons.
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.
It is very strongly recommended that you initialize all pointers either to 0 or to an object. Do not allow a pointer to have garbage in it or an address in it, that will no longer be used.
4.7 Checking Error Returns
Include the system error text for every system error message.
-
(C, C++) Check every system call for an error return, unless you know you wish to ignore errors. For example,
printf returns an error code but rarely would you check for its return code. In which case you can cast the return to (void) if you really care.
(C, C++) Check every call to malloc or realloc unless you know your versions of these calls do the right thing.
4.8 Short Methods
Methods should not be longer than a single page of code.
4.9 Document Null Statements
Always document a null body for a while statement so that it is clear that the null body is intentional and not missing code.
4.10 Comments on Comments
4.10.1 Comments Should Tell a Story
Consider your comments a story describing the system. Expect your comments to be extracted by a robot and formed into a man page. Class comments are one part of the story, method signature comments are another part of the story, method arguments another part, and method implementation yet another part. All these parts should weave together and inform someone else at another point of time just exactly what you did and why.
4.10.2 Document Decisions
Comments should document decisions. At every point where you had a choice of what to do place a comment describing which choice you made and why. Archaeologists will find this the most useful information.
4.10.3 Make Gotchas Explicit
Explicitly comment variables changed out of the normal control flow or other code likely to break during maintenance. Embedded keywords are used to point out issues and potential problems. Consider a robot will parse your comments looking for keywords, stripping them out, and making a report so people can make a special effort where needed.
Gotcha Keywords
-
:TODO: topic
- Means there's more to do here, don't forget. :BUG: [bugid] topic
- Means there's a known bug here, explain it and optionally give a bug ID. :KLUDGE:
- When you've done something ugly say so and explain how you would do it differently next time if you had more time. :TRICKY:
- Tells somebody that the following code is very tricky so don't go changing it without thinking. :WARNING:
- Beware of something. :COMPILER:
- Sometimes you need to work around a compiler problem. Document it. The problem may go away eventually. :ATTRIBUTE: value
- The general form of an attribute embedded in a comment. You can make up your own attributes and they'll be extracted.
Gotcha Formatting
- Make the gotcha keyword the first symbol in the comment.
- Comments may consist of multiple lines, but the first line should be a self-contained, meaningful summary.
- The writer's name and the date of the remark should be part of the comment. This information is in the source repository, but it can take quite a while to figure out when and by whom it was added. Often gotchas stick around longer than they should. Embedding date information allows other programmers to make this decision. Embedding the author information let's us know whom to ask.
Example
// :TODO: tmh 960810: possible performance problem // We should really use a hash table here but for now we'll // use a linear search. // :KLUDGE: tmh 960810: possible unsafe type cast // We need a cast here to recover the derived type. It should // probably user a virtual method or template.
4.11 Constructs to Avoid
-
(C, C++) The use of
#define constants is strongly discouraged, using
const is recommended instead.
(C, C++) The use of #define macros is strongly discouraged, using inline functions is recommended instead.
(C, C++) The use of typedef is discouraged when actual types such as class, struct, or enum would be a better choice.
(C, C++) 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.12 Macros
- (C, C++) In a macro definition, to eliminate ambiguity on expansion, all uses of arguments should be enclosed in parentheses and the entire macro expansion should be enclosed in parentheses. For example:
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
4.13 Debug Compile-time Switch
- (C,C++) Code used only during development for debugging, testing or performance monitoring should be conditionally compiled using #ifdef compile-time switches. The symbols to use are DEBUG, TEST 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.14 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.15 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. If the programmer wishes to not fully implement a copy constructor, then a stub copy constructor should be written and placed in the private section so no one will accidentally call it.
4.16 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.17 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.18 Default Arguments
- (C++) Where possible, use default arguments instead of function overloading to reduce code duplication and complexity.
4.19 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.20 Const Member Functions
-
(C++) It is recommended that all member functions (example: func(...) const {...}) 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.
(C++) It is recommended that all member function parameters be declared const (example: func(const int i){...}) when possible.
4.21 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.22 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.23 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.24 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.25 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.26 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.
(C++) Putting variables in the private section is preferable over the protected section, for more complete encapsulation. Use get and set methods in either protected or public if needed.
4.27 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.28 Importing Packages
- (Java) Use full package names instead of wildcards when importing to improve comprehensibility and provide context.
4.29 Exception Handling
-
(C++) In general, avoid exception handling. It is sometimes needed for third party code, but in general, use return values instead. If you need it, document the reason for using it.
5 Source Code Templates
Additional Coding Standards and Style Guides
For additional background and suggestions, there are a number of coding standard documents available on the Web; these are included for reference only and are not a part of this standard:
- Todd Hoff's C++ Coding Standards
- Recommended C Style and Coding Standards (Revised Indian Hill)
- Programming in C++, Rules and Recommendations (Ellemtel)
- Doug Lea's Draft Java Coding Standard
- AmbySoft Inc.'s Java Coding Standard
- JavaSoft Coding Standards
- Netscape Coding Standards