|
Syntext C++ Coding Style 2.0
Copyright � 1998, 2000, 2004 Syntext, Inc.
Permission is granted to any institution or individual to use, copy, modify, and distribute this document, provided that this complete copyright and permission notice is maintained intact in all copies.
Syntext, Inc. makes no representations about the suitability of this document or the examples described herein for any purpose. It is provided “
as is
” without warranty of any kind.
E-mail suggestions to: info-general@syntext.com
Chapter�1.�C++ Programming Style
This document is mostly based on the papers listed in the bibliography. They prescribe conceptual rules some of which may be settled differently (e.g, variable naming conventions). These papers also prescribe detailed explanations for the rule settlements.
This paper prescribes the
concrete
rules for Syntext project development.
In development it is necessary to follow coding conventions for the following reasons:
-
It is necessary to avoid the typical coding mistakes
-
It is necessary to unify the view of different code fragments
-
In any code fragment any project programmer has to understand semantics rapidly
-
New people can get up to speed quickly
-
In some typical situations it is convenient to apply a worked out coding style for performance increase (both human and computer).
Because of the above-mentioned reasons source code ought to satisfy the following:
-
Should be consistent (has complete structure)
-
Should be clear for reading and comprehension
-
Should be portable
-
Must not contain typical mistakes
-
Available to any programmer for modification
-
Must not lead to performance loss.
The following definitions are used in the paper:
Rule
- statement that must be strictly obeyed.
Recommendation
- statement that should be followed if it does not lead away from prescribed goals and does not contradict the common sense.
-
Rule.�
If a rule is broken it has to be explicitly documented.
-
Recommendation.�
Code is optimized only in the case of special performance requirements. Code is optimized only in extreme cases.
-
Rule.�
Code must never be copy/pasted (literally doubled). If two functionalities have the common parts, they must reuse
single
code portion.
-
Rule.�
Header files have
.h
extension.
-
Rule.�
Implementation files have
.cxx
extensions.
-
Rule.�
Automatically generated files have
.hpp
and
.cpp
extensions correspondingly.
-
Recommendation.�
Each header-implementation couple should contain not more than one class definition/implementation.
-
Rule.�
Header and implementation classes have to be named as the contained class+extension (case-sensitive). If class implementation is too large it may be split to files named like:
ClassName_section.cxx
-
Rule.�
If files contains a collection of entities (classes, functions, variables, macro), then the file is named with lowercase letters divided with underscores, e.g.
gui_geometry_structs.h(.cxx)
.
-
Rule.�
Each header file must prevent second inclusion. (See
Example�1.1, “Second inclusion prevention”
). Macro names are to follow the pattern: MY_NAMESPACE_MY_CLASS_H.
-
Rule.�
#include < >
directives must not include relative paths like
'../'
.
Example�1.1.�Second inclusion prevention
// This file contains the implementation of
// MyClass. Hence the file name is MyClass.h
// Multiple inclusion preventing macros is therefore:
#ifndef MY_NAMESPACE_MY_CLASS_H
#define MY_NAMESPACE_MY_CLASS_H
namespace MyNamespace {
class MyClass {
...
};
}
#endif // MY_NAMESPACE_MY_CLASS_H
Documentation implies
literate programming
. That is documentation to classes, functions, etc. is provided in the code, in special comments that are exported to documentation.
There are the two types of code comments:
-
Documenting comments .�
Those forming the project documentation, i.e. describe given programming component, ways to use it, and the interfaces.
-
Explaining comments .�
Those serving better (implementation) code comprehension.
Documenting comments have special form that allows documenting system to extract documentation. The concrete form is defined by chosen documentation system.
Note
At the moment of Coding Convention writing Syntext, Inc. uses
Doxygen
documentation system.
Explaining comments that use special keywords are called
key comments
(see
Example�1.3, “Documented fragment sample ”
). They provide additional semantics in tagging specific code peculiarities. Non-key comments have arbitrary form.
-
Rule.�
All comments are written in English.
-
Recommendation.�
Use "//" for explaining comments.
-
Rule.�
Keywords in key comments are separated with colon from the other words that are placed on the same line (see
Example�1.3, “Documented fragment sample ”
).
-
Recommendation.�
Key comments are added if they do not cumber the code and increase visual presentation. (For example, if a class contains a constructor and a couple of functions there is no need in key comments).
-
Rule.�
Each code file must have a comment header, presented in
Example�1.2, “File header”
.
-
Rule.�
It is necessary to distinctly document code with the key comments where special attention needed in the following form:
// KEYWORD:
author
date
: comment text
, where date is composed in YYMMDD form, for example 990619 (author and date are optional). Several line comments are acceptable, but the first string has to contain the principal statement. (See.
Example�1.3, “Documented fragment sample ”
). The following keywords are accepted:
-
TODO.�
Incomplete piece. Comments about what is to be done.
-
KLUDGE.�
Dirty hack. Comments have to contain reasons for the workaround.
-
TRICKY.�
Code is tricky and must not be modified without special attention.
-
WARNING.�
Self-explanatory.
Example�1.2.�File header
// (c) 1998 by Syntext, Inc.
//
// This is a copyrighted commercial software.
// Please see COPYRIGHT file for details.
#ifndef XX_H
#define XX_H
Example�1.3.�Documented fragment sample
// TODO: ilia 960810 : performance problem.
// We actually should use a
// hash table here, but for now we use a linear search.
-
Rule.�
All names are composed from English words and common English abbreviations.
-
Rule.�
Names must not begin with underscore "_".
-
Rule.�
Only
team-accepted abbreviations may be used in the names, or the abbreviation must be immediately documented.
-
Recommendation.�
Name must distinctly describe an entity meaning.
Note
Name that is pronounced with difficulty is a bad name. If it is difficult to make up a name for an entity then possibly design has to be revised.
-
Rule.�
If a name contains an abbreviation then abbreviation is written with lowercase letters, leaving only first letter in uppercase. (See
Example�1.4, “Only first letter of abbreviation is in uppercase. ”
).
-
Rule.�
Names of global identifiers must contain only uppercase letters, and containing words are to be separated with underscore "_".
-
Rule.�
Namespaces are named as classes (See
the section called “Class, Enums and Typedefs Names”
).
-
Rule.�
Because method or function is an action, its name must contain a verb (See
Example�1.5, “Function name example”
). Exceptions are the data member accessors.
-
Rule.�
Accepted prefix and suffix must be used. Among them:
Example�1.4.�Only first letter of abbreviation is in uppercase.
class DssslScript; // Not DSSSLScript
class SgmlEntity; // Not SGMLEntity
Example�1.5.�Function name example
int Object::checkForErrors(); // Not errorCheck() !
int Object::dumpDataToFile(); // Not dataFile() !
Class, Enums and Typedefs Names
-
Rule.�
Names of classes, typedefs, enums and structs must begin with uppercase letter. Names consisting from more than one word are composed as uppercase letter separated words in lowercase.
-
Recommendation.�
If a class name consists of more than three words then the class design should be reviewed.
-
Rule.�
Names of class methods and data members obey the same naming rules as for classes, except that they start from lowercase letter.
-
Rule.�
Private and protected data member names must end with underscore "_" (See
Example�1.6, “Example of class member names”
).
Example�1.6.�Example of class member names
class Person {
public:
enum Gender { MALE, FEMALE };
Person( Gender gender, char* name)
: gender_( gender ), name_( name ) {}
const char* name() const;
Gender gender() const;
private:
Gender gender_;
char* name_;
};
Variable and Function Names
-
Rule.�
Enum item names must contain uppercase letters with underscore separated words "_".
-
Recommendation.�
If enum is declared outside of a class, then items must have specific prefix.
-
Recommendation.�
In many cases it is recommended to add an “
invalid state
” item into an enum.
-
Rule.�
A line must not exceed 79 symbols.
-
Rule.�
Braces “
{}
” are disposed in the same column on a separate line if they correspond namespace, block or function. If braces correspond to class and keywords (
if
,
else
,
while
,
for
,
do
) then leading brace is placed on the same line as the keyword, separated by a whitespace. (See
Example�1.7, “Brace positions “
{}
””
).
-
Rule.�
Four-space indentation is used. Tabulation must not be used for indentation.
-
Rule.�
Definition blocks must be indented. (See
Example�1.8, “Definition blocks”
).
-
Recommendation.�
If nesting exceeds 4 or 5 levels then it is recommended to review the code.
-
Rule.�
Whitespace must separate parenthesis and keywords (e.g
if
,
else
,
while
,
for
,
do
).
-
Rule.�
Binary operators and operands are separated with whitespace.
-
Recommendation.�
Class members should be declared in the following order:
public, protected, private
.
-
Recommendation.�
Public constructors and destructors should be declared before the member functions.
-
Rule.�
Use classes, and not structs.
Example�1.7.�Brace positions “
{}
”
char* Object::fooBar()
{
while (true) {
// ... something
}
}
Example�1.8.�Definition blocks
DWord dword_;
DWord* pDword_;
char name_;
-
Rule.�
Function definition/declaration must explicitly define return value type.
-
Rule.�
Function definition/declaration must be given in the following form: return value type, function name, leading parenthesis and the first argument should be placed on one line. If there is enough room then
all
the other arguments and closing parenthesis may also be written on the same line as the function name. Otherwise each additional argument must be written on a separate line (with the closing parenthesis directly after the last argument). (See
Example�1.9, “Function declaration example”
).
-
Rule.�
Always write the left parenthesis after a function name. (See
Example�1.9, “Function declaration example”
).
Example�1.9.�Function declaration example
int doComplexThing(int intValue,
char* charPointerValue,
int* intPointerValue);
-
Recommendation.�
Class data members are at least
protected
, preferably
private
.
-
Rule.�
Member function implementation must not be given in the class declaration. Trivial functions that do not increase dependencies of the header (do not require additional
#include "...."
) may have implementation in class declaration header.
-
Rule.�
Member functions which do not change object state must be declared as
const
.
-
Recommendation.�
Class that uses
new
for object allocation should declare copy constructor and assignment operator. Classes which semantics does not imply copying should declare
private
copy constructor.
-
Rule.�
All the base classes that have virtual functions must have virtual destructor.
-
Recommendation.�
In the derived classes
virtual
key word should be used for the reimplemented virtual functions.
-
Rule.�
If a class has one of the following functions: copy constructor, assignment operator, default constructor, then it must have all of the mentioned functions.
-
Rule.�
Assignment operator must not change object if it is assigned to itself.
-
Rule.�
Exceptions must not be used in destructors.
-
Recommendation.�
Assignment operator must return
const
reference.
-
Recommendation.�
Operator redefinition must be done with care and not change semantics.
-
Recommendation.�
Public member functions should not return non-const reference and pointers for data class members.
-
Rule.�
Class members that are initialized in constructor should be initialized as in the example
Example�1.10, “Member date initialization ”
.
Example�1.10.�Member date initialization
Person::Person(Gender gender, char* name)
: gender_(gender), name_(name) {}
// The following constructor has worse performance in comparison
// to the previous one.
Person::Person(Gender gender, char* name)
{
gender_ = gender;
name_ = name;
}
Note
If not otherwise mentioned the rules and recommendations are also applied to member functions.
-
Recommendation.�
Functions with the large number of arguments should be avoided.
-
Recommendation.�
It is necessary to avoid large and complicated functions (more than 50-60 lines).
Note
If function is large, then its comprehension is complicated.
Bugs are localized easier in short functions.
-
Rule.�
Function arguments must be passed as
const
references and not by value.
-
Rule.�
Formal parameter names must be specified in declarations and coincide with the names in definitions.
-
Rule.�
Not any function must return a reference or a pointer to its local variable.
-
Rule.�
Macros must not be used in the situation when inline-function may be used.
-
Rule.�
Constants must be defined with
const
or
enum
and never with
#define
.
Note
This makes debugging easier
-
Rule.�
Numerical values (except marginal like 0 or 1) must not be used in the code, but defined with constants or enums.
-
Rule.�
Each variable must be declared in a separate statement. (See
Example�1.11, “Variable declaration ”
).
-
Recommendation.�
Variables must be declared in minimal scope. This recommendation may be neglected in specific cases of optimization (like cycle optimization).
-
Rule.�
Always
initialize variables in the declaration or by their first usage. Always. Every time.
-
Rule.�
When possible initialization, but not assignment must be used (See
Example�1.12, “Assignment in initialization ”
).
-
Rule.�
Unsigned
must be always be used for variables that semantically may not have negative values.
Example�1.11.�Variable declaration
char* i; // Correct
char j; // declaration
char* i, j; // Error prone!!!
Example�1.12.�Assignment in initialization
void fooBar(complex const& z)
{
complex z1 = z;
complex z2;
z2 = z;
// Initialization of z1 has better performance than
// that of z2
}
-
Rule.�
Qualifiers "
*
" and "
&
" in declaration/definitions must not be separated from type names.
-
Rule.�
NULL
macro must not be used. It is necessary to use
0
instead.
-
Rule.�
When using pointers to functions
typedef
must be used (for syntax simplification).
Expressions and Statements
-
Rule.�
Empty cycle body must always be commented and placed on a separate line. (See
Example�1.13, “Empty cycle body”
).
-
Rule.�
Constants are placed on the left side of "==" expressions. (E.g.
if (6 == errorNum) {
)
-
Rule.�
Statements with simultaneous assignments must always be commented and taken in the additional parenthesis. (See
Example�1.14, “Assignments in statements”
).
Note
Without such a comment a code may easily be "corrected" by another team member.
-
Rule.�
Code following
case
must
always
end with
break
if even if it is the last
case
in a
switch
. If a “
fall through
” is implied, it
must
be commented. (see
Example�1.15, “Switch-statement example”
).
-
Rule.�
Switch
must
always
have
default
(see
Example�1.15, “Switch-statement example”
).
-
Rule.�
If local variables are used in
switch
then this part of code should be placed into a block. (See
Example�1.15, “Switch-statement example”
).
-
Rule.�
Conditions in
"? :"
operator must be taken in parenthesis for the sake of readability; if the statements are long then they must be placed in separate lines. (See
Example�1.16, “Operator "? :"”
).
-
Rule.�
One line must contain only one statement except for the cases when they are very close in meaning.
-
Recommendation.�
When in doubt of priorities parenthesis, must always be used.
Example�1.13.�Empty cycle body
while (*dest++ = *src++)
; // VOID
Example�1.14.�Assignments in statements
if ((aBool = bBool)) { // ASSIGNMENT
...
}
Example�1.15.�Switch-statement example
switch ( number_ ) {
case 1:
os << "Red";
// FALL THROUGH
case 2:
{
int tmp_var;
....
}
break;
default:
throw BadCondition();
break;
}
Example�1.16.�Operator "? :"
(condition) ? func1() : func2();
or
(condition)
? long statement
: another long statement;
-
Rule.�
Malloc
,
realloc
, and
free
must not be used (except for
specific
low-level implementations).
-
Recommendation.�
There should be minimum of global identifiers.
-
Recommendation.�
Pointers to freed memory must always be assigned a value (in most cases 0).
-
Rule.�
Static objects with constructors are deprecated. Use singletons instead.
-
Rule.�
No assumption must be taken on physical representation of the fundamental types (and pointer), and their mutual correspondence.
-
Rule.�
char
must not be assumed as
signed
or
unsigned
.
-
Rule.�
No assumptions must be taken on type specifics in overflow cases.
-
Rule.�
No assumptions on the order of member constructor execution must be taken. (See
Example�1.17, “The order of object initialization may be undefined. ”
).
-
Rule.�
No assumption on the initialization order of static objects must be taken.
-
Rule.�
No assumption that function parameters are passed in any special order must be taken.
Example�1.17.�The order of object initialization may be undefined.
X::X(int y) : j(y), i(j) // No! j may not be initialized before i !
{
// Some code
}
General Principles of Interaction Within Developer Team.
-
Rule: Do not duplicate what was done.�
If some functionality is already implemented, it must be used. Creating alternatives is prohibited. If the interface or the implementation of the functionality does not satisfy a developer he should talk about the enhancements, and must not create yet another version.
-
Rule: Ask others if it was done.�
If a programmer suspects that the problem faced has already been implemented then he must ask the team on e-mail on available solution.
-
Rule: Inform others on what was done.�
Members must inform team on new reusable components implemented.
Essential Conditions for Code Development
Create early and not often:
-
Version control system (e.g. CVS)
-
Unified debugging system
-
Project tracking system
-
Bug-tracking system
-
Responsible build-master (
make
environment,
Makefile
support).
-
Responsible for every module. Only the responsible may approve committing module modifications.
Ellemtel Telecommunication Systems Laboratories
.
Programming in C++, Rules and Recommendations (1993-12-31, Rev. C)
.
Available online.
Todd
Hoff
.
C++ Coding Standard (October 31, 1997)
.
Available online.
Leo
Michelotti
.
Light and Non-Controversial Coding Styles (February 14, 1997)
.
Available online.
Bjarne
Stroustrup
.
The C++ Programming Language, 3rd Edition
.
Copyright � Addison-Wesley, 1997.
0-201-88954-4.
|