C++ Style Guide
1. Introduction
-
This document contains the coding standard of Graphisoft, which mainly sets the
formal requirements for writing source code. It also describes a few rules and
recommendations for the content. You have to obey these rules; by default every
declarative or imperative sentence is a rule, except where 'recommendation' (or
avoidable, optional, etc.) is explicitly stated.
2. Naming conventions
-
Using prefixes besides the ones allowed below is forbidden.
-
#define
names
-
Should be written in capitals; names consisting of more than one word should be
separated by underscores (_).
E.g.: NULL, PLATFORM, DEBUG_LEVEL_2, REFERENCE_CHECK
NULL should be used to denote 0 pointers.
-
Type names (struct,
enum, typedef,
class, namespace, template
parameter)
-
Starts with an uppercase letter; every new word should also start with an
uppercase letter. For interface names the "I" prefix is compulsory.
E.g.: Vector2D, Array, SymbolSet, Char, IComparable.
-
Variable names
-
Starts with a lowercase letter; every new word should also start with an
uppercase letter. For member variables the "m" prefix (e.g.: mLength), for
globals the "g" prefix (e.g.: gWindowCount) is allowed. Globals can be
also indicated with the :: global scope.
E.g.: size, isDeletable, newFont.
-
Constants (const,
enum)
-
Starts with an uppercase letter; every new word should also start with an
uppercase letter. Names of enums defined in the common scope should differ, so
the use of prefixes is allowed here.
E.g.: Underscore, DefaultSize.
_ -
Function names
-
Starts with an uppercase letter; every new word should also start with an
uppercase letter.
E.g.: Print, SetPort.
-
Method names
-
Starts with an uppercase letter; every new word should also start with an
uppercase letter.
Preferably you should use verbs or structures containing a verb for names (e.g. verb + object). E.g.: Move, FindLast, AppendName, DeleteIcon.
The verbs Set, Get, and Has should be used in method names setting and retrieving attribute information. E.g. SetSize, GetSize, IsEmpty, HasIcon.
Conversion methods should have names starting with the 'To' prefix, and without verbs. E.g.: ToString, ToDouble.
-
Parameter names
-
For input, output or input/output parameters it is allowed to use the in/out/io
prefixes (e.g.: inString, outText, ioLength).
Using underscores (_) is not forbidden, but please avoid it if possible. It cannot be used as the first or last character of a name.
Standard C function names (e.g. floor, abs) shouldn't be used as names.
3. Expressions
-
Space should be used in front of and behind the following binary operators:
- arithmetical: *, /, %, +, -, <<, >>
- logical: &&, ||, ==, !=, <, <=, >, >=
- bit manipulating: &, ^, |
-
assignment: =, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^=
-
a = b + c % d;
x += y;
double r = 25 * b / 2 + 4;
-
No spaces are allowed in front of and behind the following binary operators:
- Scope resolution: className::member
- Member selection: object.member, pointer->member, object.*member, pointer->*member
-
No spaces are allowed in front of and behind the following unary operators:
- pre, post / increment, decrement: ++i, --i, i++, i--
- subscripting: array[25]
- complement: ~x
- logical not: !x
- unary minus, unary plus: -x, +x
- address of: &var
- dereference: *p
-
global scope: ::globVar
-
Space should be used in front of and behind the following (special) unary
operators:
- function calls: SetItem (25, item)
- casting: (char*) p
- run-time type identification: typeid (Vector)
- size of object, size of type: sizeof (*v), sizeof (Vector)
- allocation, deallocation: new type, delete p, delete [] p
- exception throwing: throw Exception
-
Function calls should have a space in front of the opening parenthesis of the
parameter list, and behind every comma separating the parameters:
-
sin (PI / 2);
double average = Average (i, 25 * j, 45);
Stop (); -
When comparing with constants (e.g. i == 5) the constant should be the
second operand.
-
In conditional expressions the condition should be parenthesized, placing a
space in front of and behind the '?' and the ':' operators:
-
Int32 val = (a < 5) ? 0 : a * 2;
-
Put a space behind the comma (sequencing) operator:
-
for (int i = 0; i
< 10; i++, j++)
-
Casting in C++:
-
p = static_cast<Vector*> (pVec);
Vector& v = dynamic_cast<Vector&> (*pVec);
-
C-style casting (can be used only in C code, or C code compiled as C++):
-
pc = (char*)
p;
pc = (int*) (q->c + 3); -
When assigning values to bool variables
the logical expression should be parenthesized:
-
bool b = (i > j);
-
Don't use automatic bool conversion;
write the conditions explicitly in the code:
-
unsigned int i = f ();
if (i != 0) { // instead of: if (i)
...
}char* p = g ();
Also, do not use the bool result of logical negation. E.g. instead of !i please use i == 0, and instead of !p please use the p == NULL condition.
if (p != NULL) { // instead of: if (p)
...
}
(Of course, bool variables and expressions can be negated, e.g. !b).
-
Do not compare bool variables and
expressions to true or false;
use the value of bool or its negated
value:
-
bool b = true;
if (b) { // instead of: if (b == true)
...
}if (!b) { // instead of: if (b == false)
...
} -
Logical expressions consisting of many parts should be placed on multiple
lines, and you should also align the sub-expressions or the logical operators.
Also leave an empty line in front of the body:
-
if ((a > 2 && b < 1) ||
(c > 10 && d == 0) ||
(e == -5) ||
(f < 5 && f > 1))
{
// body
}
-
You can use chain assignment only for closely related variables of the same
type (e.g.: i = j = 0).
-
Put parentheses around rarely used operator combinations.
4. Control flow statements
-
if-else
-
if (condition)
statement;
-
if (condition) {
// body
}
-
if (condition)
statement1;
else
statement2;
-
if (condition) {
// body
} else {
// body
}
-
if (condition1) {
// body
} else if (condition2) {
// body
} else if (condition3) {
// body
} else {
// body
}
-
if (condition1) {
if (condition2)
statement1;
else
statement2;
}
The condition shouldn't contain any assignment. -
if (condition)
-
switch
-
switch (key) {
case Value1:
// body
break;case Value2:
// body
// no breakcase Value3:
case Value4:
// body
break;case Value5:
}
// body with local variables
}
break;default:
// body
break;
}
Adding a "no break" comment is compulsory if after executing the body of a non-empty case branch execution will continue at the next case branch.
You shouldn't add break after unconditional return and throw statements.For short-body similar case branches (e.g. conversion) you can use the more readable compact switch form:
switch (code) {
case 'I': return 1;
case 'R': return 2;
case 'B': return 3;
case '\n': return 4;
default: return 0;
} -
for,
while,
do-while
-
for (Int32
i = 0; i < 100; i++)
statement;
-
for (Int32
i = 0; i < 100; i++) {
// body
}
-
for (Int32
i = 0; i < 100; i++)
; // empty body
If the loop variable is only needed inside the loop, then you can define it in the header of the for loop as in the examples above.
If you leave out any of the statements from the head of the for loop, then you should leave a space between the '(', ';', or ')' characters. E.g.: for ( ; ; )
-
while (condition)
statement;
-
while (condition) {
// body
}
You shouldn't use assignments in the condition.
-
do
statement;
while (condition);
-
do {
// body
} while (condition);
You shouldn't use assignments in the condition.
-
for (Int32
i = 0; i < 100; i++)
-
try-catch-throw
-
try {
// body
}
catch (Type1 exception) {
// body
}
catch (Type2) {
// body
}
catch (...) {
// body
throw; // rethrow
}
catch (const Type& exception) or
catch (const Type& e) or
catch (const Type&)(if Type is not a basic type).
5. Variable declarations
-
Declare all variables in the tightest scope. E.g. if you have a utility
variable which is needed only in an if branch,
then declare it there. Static variables of functions should be declared at the
beginning the function. Do not use variables with the same name within the same
function.
Caution: the constructor of variables declared in the scope of a (for, while, do) loop is called for each iteration. Sometimes this is what you need, but you may run into performance problems.
-
Class-type variables (objects) should be declared where they can be properly
initialized (the default constructor + an assignment later is not as efficient
as a proper initialization using the appropriate constructor.) The basic type
should be declared at the beginning of the tightest scope. If a (local)
variable cannot be properly initialized when declared, then don't give it a
dummy value (e.g. 0), so that the compiler can shout if you forgot to
initialize the variable in one of the branches. If the compiler gives a false
warning then indicate this in a comment at the place of the unnecessary
initialization.
-
All variables should be declared (and initialized) on separate lines. This is
obligatory for global (static) and class data member variables, pointers and
references. An exception to this rule can be made for tightly related local
variables. E.g.:
-
double x, y, z;
-
Variables shouldn't be defined in line with the opening brace ('{') of
the scope.
-
Declaring and initializing local and global (static) variables:
-
Int32 index = 3;
The '*' for pointers and the '&' for references should be bound to the type:
-
char* ptr = NULL;
Int32& index = oldIndex;
Don't leave a space between the array variable name and the '[' character:
-
char array[20];
char* numbers[] = { "One", "Two", NULL };
-
The declaration of class data members is similar to that of local variables,
though the initialization is (clearly) done somewhere else, and alignment is
stricter because there are more members. See the chapter
Data member declaration about data member declaration, and chapter
Implementing methods of embedded classes about implementation issues.
-
Global variables and constants should always always be accompanied by
comments describing their usage, and give great care when choosing their name
(it shouldn't be k, myDrw or newTKDef). If only a longer name can describe the
role of the variable, then use that. Don't use (many) global variables; put
everything into namespaces.
-
If a "variable" is constant then declare it const:
-
const Vector& v = translation;
-
Do not use "naked" values (literals) in the code; assign a constant to each of
them.
- Naming conventions: => see chapters Variable names and Constants.
6. Functions
-
Declaration
-
Place one (or two if the first gives only one space distance) tab between the
type of the return value and the name of the function. Also leave a space in
front of the opening parenthesis of the parameter list and behind each
parameter-separating comma. E.g.:
-
void DrawPixel (Int32
x, Int32 y);
short GetVersion (void);
If you have more than one modifying keyword, the function name can be moved to a new line:
-
static int cdecl
SetPointer (char* p);
-
If you have default parameters, put a space in front of and behind the '=':
-
void PrintNumber (Int32
n, short radix = 10);
-
If you have many parameters and the function declaration doesn't fit into one
line, every parameter should be put into a new line and the names and types
should be aligned. E.g.:
-
String ConcatenateName (const String* part1,
const String* part2,
const String* part3,
const String* part4,
bool appendNewLine = false);
-
Use const whenever it is possible.
-
You should always write void if the function
doesn't have any parameter. Except: default constructor, destructor and the
operators which don't have parameters by definition.
-
Do not use references to pass values back and to initialize variables. Use
pointers instead. On the other hand references may be used for value or object
modification (input/ouput parameters).
-
Pass bool parameters only in the simplest cases,
use enums instead.
-
Operator overloading can be used only for natural conversions. The operator
should immediately follow the operator keyword.
-
Naming conventions: => see Function
names
-
Place one (or two if the first gives only one space distance) tab between the
type of the return value and the name of the function. Also leave a space in
front of the opening parenthesis of the parameter list and behind each
parameter-separating comma. E.g.:
-
Implementation
-
Style:
-
void PrintNumber (Int32
n, short radix)
{
// body
}
-
The function header should follow the rules described above for
function declarations.
-
You should write the body of the function indented by one tabulator field to
the right.
-
You should leave two empty lines behind the closing '}' of the function.
-
You should write one statement per line.
-
Functions shouldn't be longer than 80 lines.
-
Try to avoid too compact structures, very complex expressions and magical C
tricks.
-
Conditional expressions should be kept maximum 3 levels deep; try to separate
the code to separate (inline) functions if more
depth is needed.
-
Try to avoid using many (more than one) return
statements. Also, avoid return statements
in the middle of the function body, except the conditional returns at the
beginning of the function.
-
Use Asserts to check incoming parameters
(preconditions) at the beginning of the function body.
-
Declaration of local (or static) variables is discussed in the
Variable definitions chapter.
-
Style:
7. Type definitions
-
struct
-
struct Vector {
double x; // x coordinate of the Vector
double y; // y coordinate of the Vector
double z; // z coordinate of the Vectorbool isNormalized; // if it is true, then the vector is normalized
};
Other rules concerning data member declarations are the same as what you can find in the Data member declaration chapter for classes.Place an empty line between the data members and constructors if you need constructors. E.g.:
-
struct Point {
Int32 x; // x coordinate of the Point
Int32 y; // y coordinate of the PointPoint ();
Point (Int32 xc, Int32 yc);
};
Other rules concerning method declarations are the same as what you can find in the Method declaration chapter for classes.
See the chapter Embedded classes and structures for information on embedding structures into classes.
-
union
-
Unions should comply with the same rules as srtucts.
Use unions only for optimization for space.
-
enum
-
enum Status { OK, Error };
// return value of many methods
enum DataType { // possible data types of the interpreter
Int,
Real,
Bool,
String,
Object,Void
};
-
enum Token
// tokenizer constants
{
Integer = 25,
Real = 48,
Character = 701
};
Take into consideration that the physical size of an enum can be different on each platform. Because of that e.g. the length of a structure containing an enum can vary, causing problem if you write it out in binary form. To avoid this set the size of the enum to the size of int before compilation.
Give value to the enumerators if the enum will be written into a file.
-
typedef
-
typedef Int32 Offset;
typedef unsigned Int32 Index;
typedef VectorFS<double, 3> Vector3D;
8. Class definitions
-
Base structure
-
Visibility -
allowed is the developer's viewpoint (private,
protected, public), and the user's
viewpoint (public, protected,
private). There may be only one of each section.
E.g.:
-
class Vector {
private:
// private partprotected:
// protected partpublic:
// public part
};
-
class Vector {
public: // predefinitions
typedef double Coord;
// other public definitions also used by the private (protected) partprivate:
// private partprotected:
// protected partpublic:
// public part
}; -
Template
classes
-
template <class Type,
int BufferSize = 16>
class Array {
// body
};Template arguments should be treated similarly to function arguments.
-
Inheritance
-
class Derived: public
Base1,
public Base2,
protected Base3,
private Base4
{
// body
};
Use the public virtual Base ordering for virtual inheritance.
The opening brace in case of single inheritance should go to the end of the header line (class ...). -
Naming
conventions
- Class names => see Type names
- Template parameter names => see Type names
-
Visibility -
allowed is the developer's viewpoint (private,
protected, public), and the user's
viewpoint (public, protected,
private). There may be only one of each section.
E.g.:
-
Body
-
private
section - typical layout, recommendations, rules
-
class String {
private:
// definitionstypedef short Symbol;
enum InternalStatus { OK, Error };
// static (class) data membersstatic const Int32 MaxSize; // short description
static char defChar; // short description// normal (instance) data members
char* str; // short description
Int32 size; // short description// static (class) functions
static unsigned char* ToPascalString (const char* cStr);
// normal (instance) member functions (methods)
void Clear (void);
Int32 SkipSign (Int32 from) const;
inline Int32 SkipDigits (Int32 from) const;
virtual void Print (void) const;
protected:
// protected partpublic:
// public part
};
Put disabled methods (e.g. constructors) to the beginning of the normal method list with a // disabled comment. E.g.:
-
String (const String& source);
// disabled
String& operator= (const String& source); // disabled
private:
friend class Menu; // must be friend because ...
If the friend class only needs a few private methods or data, then you should put the friend declaration close to those, so that the user would see what the friend class uses from our class. (This ruins the immediate visibility of those classes which can access our class.)
You should explain in a short comment the reason behind the friend declaration (in the same line).
(The exceptions to this rule are those public friend operator functions, which are not normal methods so that the compiler could assure the symmetry of the operator. E.g. operator+ (const Vector& v1, const Vector& v2)friend function of the Vector class). -
protected
section - typical layout, recommendations, rules
-
There aren't any special recommendations and rules for the protected
section, except the following recommendation:
Use protected data members sparingly; try protected (even inline) methods instead.
-
public
section - typical layout, recommendations, rules
-
class String {
private:
// private partprotected:
// protected partpublic:
// definitionstypedef char* CStr;
enum SymbolType { ASCII, WideChar };
// declaration of optional exception classes (types)
// static const (class) data membersstatic const char NewLine = '\n'; // short description
static const char MaxLength = 256; // short description// static (class) functions
static char* GetClassName (void);
// Constructors
String ();
String (const char* cStr);
String (const String& source);
~String ();
String& operator= (const char* cStr);
String& operator= (const String& source);// normal (instance) member functions (methods)
void Delete (Int32 idx);
void Delete (Int32 from, Int32 range);
void DeleteLast (void);inline void Clear (void);
void Replace (Int32 from, Int32 range, char ch);
inline char& operator[] (Int32 idx);
inline char operator[] (Int32 idx) const;virtual Int32 Read (IChannel& ic);
};Do not use public non-const data members in classes.
-
Data member
declaration
- Data members (one per line) should appear below each other, and put a short description after each member.
-
If you have many data members, align them. Leave at least two spaces between
the type and the name. E.g.:
-
char*
str; // points to the allocated
string (if not NULL)
UInt32 size; // size of the string
- If you have many data members, leave an empty line between the logical groups.
-
If a member has many modifying keywords (static,
const, mutable), this is the correct
order:
- static const
-
mutable const
-
Method declaration
- You should not put comments behind the method declarations (the public and protected methods should be described in the documentation, the private methods in the implementation to make the class easily understandable).
-
If you group the methods, align the names of the methods, and preferably leave
a tab (or two if one yields to only one space) between the return type and the
name. Also align the parameters except when the name is very long. E.g.:
/* IO methods */
-
virtual Int32
Read (IChannel& ic);
Int32 ReadQuotedString (IChannel& ic, bool skipLeadingWhiteSpace = true);
Int32 ReadLine (IChannel& ic);
virtual Int32 Write (OChannel& oc) const;
-
If a method has many parameters, and the declaration doesn't fit into one line,
every parameter should appear in a new line, the types and the names of the
parameters should be aligned. E.g.:
-
virtual Vector3D ComputePosition (const
Matrix& rot,
Mode mode,
double x,
double y,
double z) const;In this case you can leave an empty line after the method declaration if you feel the class would be more perspicuous.
-
Style of template method declarations:
-
template <class U,
class V>
inline Pair& operator= (const Pair<U, V>& p);
-
If you group the methods logically, you can place a short group description in
front of or an empty line between the method groups. Place an empty line in
front of and behind the group description, and also indent it with one tab.
E.g.:
-
/* Compare methods */
bool operator== (const char* cStr) const;
bool operator!= (const char* cStr) const;/* Conversion methods */
const char* ToCStr (void) const;
const char* ToCStr (Int32 from, Int32 range) const;
-
Place other constructor-like methods (such as operator=)
in the group of constructors. Other examples for these methods are (with their
recommended names):
- static String* NewInstance (void); - creates a new String, but you can take its address (opposed to the constructor)
- String& Assign (const char* charPtr, Int32 charCount); - like operator=, but it can have many parameters
- virtual String* Clone (void) const; - copies the String
-
Write the default constructor and destructor with an empty - no
void - () parameter list, so that it can be found easily and
emphasizes that these don't have any parameters by definition. (You should
write the same way operator functions without parameters.) For all other
methods and functions which don't have parameters you should always write
the void keyword.
-
Always align the names of constructors, even if you use the explicit
or inline keywords. E.g.:
-
String ();
explicit String (char c);
String (const char* cStr);
inline String (const char* charPtr, Int32 charCount);
explicit String (unsigned char* pStr);
String (const String& source);
-
The compulsory parameter name for the copy constructor and the assignment
operator is source:
-
String (const
String& source);
String& operator= (const String& source);
-
You should write the virtual keyword in front of
all such methods for better navigation, even if the method inherited this
attribute.
-
Style of abstract virtual methods:
-
virtual
void Draw (void)
const = 0;
-
In case of many modifier keywords (static,
virtual, inline, explicit,
friend) the order should be:
- static inline
- virtual inline
- explicit inline
- friend inline
-
You should not write the method implementation into the class
declaration (except the short methods of embedded classes).
- In all other points (placement of parameters, default parameters, etc.) the declaration of methods follow the rules of function declarations.
-
Embedded
classes and structures
-
You should place embedded (utility) classes and structures in the declaration
part of the class, and indent them with one tab. E.g.:
-
class Vector {
private:
// definitionsstruct RealPair {
double r1; // first value
double r2; // second value
RealPair (): r1 (0), r2 (0) {}
RealPair (double val1, double val2) r1 (val1), r2 (val2) {}
};...
};
Avoid writing large embedded classes, create a well hidden namespace instead (e.g. for a String class with the name StringPrivate), and put the embedded class there. -
Naming conventions
- Type names
- Data member names => see Variable names
- Constants
- Class function names => see Function names
- Method names
-
private
section - typical layout, recommendations, rules
9. Class implementation
-
Static (class) members
-
Int32
Array::defaultInitialCapacity = 100;
const Int32 Array::MaxInitialCapacity = 1000000000;
const double Array::DefaultAllocationUnit = 100.5;
Declare static data members at beginning of the source (.cpp) file.
-
Methods
-
Place two empty lines between the method implementation functions. Put the
public inline functions three empty
lines behind the end of the class in the header file.
-
The order of methods in the implementation file should be the same as in the
class declaration. You should write the constructors first, then the other
public methods (as in the class declaration). It is practical to
implement the private methods after the
public methods, and the protected methods
where they fit in most logically.
-
Style of constructors
-
Array::Array (Int32 initialCapacity,
Int32 maxSize):
base1 (initialCapacity),
base2 (0, 25),
member1 ("", 40),
member2 (1)
{
// body
}
-
Style of template methods:
-
template <class Type>
inline Int32 Array<Type>::GetSize (void) const
{
// body
}
-
The method declaration
chapter defines the style of the method header.
-
You should use the this pointer to access the data
members and functions (e.g. this->x)
only if this has a distinctive role. Never use it for static members. Also, you
don't have to use it for accessing the members of the ancestor class(es). When
accessing names outside of the scope of the class, you have to use the
scope. E.g. call a (maybe OS) Sleep () function this way: ::Sleep ().
-
Do not use method arguments which have the same names as data members if
possible. If you couldn't find any other logical name, then please use the
this pointer to access the data members.
-
Recommended style for writing an assignment operator (operator=):
-
Point2D& Point2D::operator= (const
Point2D& source)
{
// testing whether source is NULLif (this != &source) {
BaseClass1::operator= (source); // if Point2D has base class named BaseClass1
BaseClass2::operator= (source); // if Point2D has base class named BaseClass2x = source.x; // own data member
y = source.y; // own data member
}return *this;
}
- In other points the method implementation follows the rules of function implementation.
-
Place two empty lines between the method implementation functions. Put the
public inline functions three empty
lines behind the end of the class in the header file.
10. Namespace
-
namespace CLib {
void* MemCopy (void* destPtr, const void* srcPtr, unsigned Int32 count);
void* MemMove (void* destPtr, const void* srcPtr, unsigned Int32 count);
void* MemSet (void* destPtr, int filler, unsigned Int32 count);
}
If you define a (bigger) class in the namespace, then indenting is not always practical. In this case use the following style:
-
namespace Math {
class Vector {
// body
};
} // namespace Math
Don't open the namespace again for the
implementation of the functions. E.g. write the implementation of the MemCopy
function above outside the namespace:
-
void* CLib::MemCopy (void*
destPtr, const void*
srcPtr, unsigned Int32
count)
{
// body
}
This error is very rare in class method implementations (as the given method should be declared up front in the class), so in that case you can write the implementation outside the namespace, open the namespace again, or use the using directive.
Except for the main () function everything should go into a namespace (at best you (should) open the given namespace ). This is important to avoid potential name clashes, especially if you use external libraries.
Use the using namespace X directive mostly in the implementation file, it should appear in the headers only if it is really necessary (e.g. if you would like to have your base module in an open namespace). Don't use "separate" using declarations in headers, as these declarations will be involuntarily inherited by those files which #include the header.
The main reason behind the usage of the using directive is to put it only into those files where the given name(s) appear quite often, e.g. because it is a base unit (type, constant, etc.) of that area. Don't put using in only for a couple of abbreviations, or put it maybe just into a smaller scope, e.g. inside a method. Basically we use namespaces to separate and distinguish between the modules and the names declared in them, so limit the number of using directives both in quantity and scope.
Use namespace alias only where it is really necessary.
11. Preprocessor directives
- Avoid #elifs.
- Avoid complex #if expression variations. (But this may be necessary for #if DEBUG_LEVEL > 5.)
- Avoid macros. Use inline and / or template functions and methods.
- Avoid #defines; use consts or enums instead.
-
#if defined XXX
// body
#else
// body
#endif
Do not indent "normal" C++ code in conditional preprocessor statements; but indent other preprocessor statements with one tab.
Put comments into structures which are longer than two screens, so that matching directives could be easily found.
Do not use the short #ifdef and #ifndef directives.
Leave a space between the #include and the file
name:
-
#include "Array.hpp"
12. Writing comments
-
The language of the comments should be English; avoid bad words. Do not use
accented characters.
-
You should use the following style for comments; any other form is forbidden:
// *****************************************************************************
// One sentence summary
// Module / project name, platform
//
// Namespaces: Contact person:
// DG BM
// LibPart PJ
//
// [SG compatible] - if it really is
// *****************************************************************************Any code can come only after this, including the #if !defined ARRAY_HPP directive as well.
-
The length of the full comment line is 120 characters.
-
You should always indicate whom the namespace belongs to. For classes you may
indicate the owner.
-
You should always put comments behind data members in class declarations:
possibly in the same state what that data member is used for. The same is valid
for global variables. Methods are not commented in class declarations.
-
Comment unused function parameters with /* ... */;
do not use the #pragma unused directive. Use the
UNUSED_PARAMETER macro instead if it is needed in the release build.
-
For the non-public functions of the module you should always explain the
meaning of non-trivial parameters and the return value. E.g. for indices always
indicate the range (starts from 0 or 1, any special values, etc.). Example:
// -----------------------------------------------------------------------------
// Function: Reads ...
//
// inHandle: Contains the text. If nil read from 'inFile'
// inFile: Input file description
//
// return value: OK or ERROR
// -----------------------------------------------------------------------------OSErr ReadTemplate (Handle inHandle, FileDefRec inFile)
{
...
}
-
You should always indicate with three exclamation marks if you leave something
unfinished:
-
n = 5; // !!!
st; It will come from database
-
You can also put optional section descriptions in between the lines of the
source code, beginning at the current tab depth. You can also add short
explanations to the end of the source line by adding a tab after the semicolon;
or, if there are more of those, you can align them with tabs. E.g.:
-
n = 5;
// Searching will follow
for (i = 0; i < n; i++) {
if (a[i].GetCode () == pattern) {
b[i].SetValue (a[i].GetValue ()); // Found
}
}
- For unusual solutions (leaving break out, empty loop, etc.)
- If it would take too long to understand the code for someone else without the comment.
- If something is forbidden or not recommended for others.
Do not let the comments break the flow of the code, and don't break up the code.
13. File structure
-
Always use 4 spaces as tabs in all files.
-
The maximum length of the lines is 120 characters. Statements (function and
method bodies) shouldn't even get close to this number.
-
The body of a header file should be braced with the following preprocessor
structure. The name is made up of the namespace identifier, the file name and
the HPP suffix, in capitals and separating them with underscores (_). E.g.:
-
#if !defined
IO_LOCATION_HPP
#define IO_LOCATION_HPP// body
#endif
This is a special case for preprocessor structures; do not indent the lines within.
-
Write all the #includes at the beginning of the
source file, and all the forward declarations in case of header files. E.g.:
-
// -------------------------------- Includes
---------------------------------
#include "Base.hpp"
#include "Array.hpp"
#include "Stack.hpp"
// ---------------------------- Forward declarations ------------------------------
class SymbolSet;
-
The maximum length of file names is 31 characters. File names are case
sensitive, and use the extensions .cpp and .hpp, or .c and .h, depending on
whether they can be used in pure C code. The file names should be given case
sensitive in all references (#include, makefile).
-
Relative paths in #includes are allowed, but the
".." path component is forbidden. Furthermore it is not allowed to
reference module roots (e.g.: #include
"GSRoot/Array.hpp"), only subdirectories inside them.
-
Preferably every class has its own header and source file, but if you have many
closely related classes, put them into one file. A template class and its
specialized versions should also be kept in one file. On the other hand, avoid
placing a base class and its descendants into one file.
-
Every header file #includes only what the class
declaration needs: the ancestor classes and the data member types. If a type is
used only as a pointer, reference, or method return value in the class, then
use forward declarations instead of #include, to
reduce the number of dependencies and included (and opened at compile time)
files.
-
The files of each subsystem (module, package) should be kept in a separate
subdirectory.