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.
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.
-
#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).
Acronyms are an exception to the capitalization rule, e.g. ISO_Standard, where
ISO is written in all caps.
3. Expressions
-
Space should be used in front of and behind the following binary operators:
-
arithmetical: *, /, %, +, -, <<, >>
-
logical: &&, ||, ==, !=, <, <=, >, >=
-
bit manipulating: &, ^, |
-
assignment: =, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^=
E.g.:
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:
long val = (a < 5) ? 0 : a * 2;
Avoid embedding such conditional statements if possible.
-
Put a space behind the comma (sequencing) operator:
for (int i = 0; i
< 10; i++, j++)
Please don't overuse the comma operator.
-
Casting in C++:
You are not allowed to use C-style casting in C++ code.
-
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:
-
Don't use automatic bool conversion;
write the conditions explicitly in the code:
-
Do not compare bool variables and
expressions to true or false;
use the value of bool or its negated
value:
-
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
}
Complex expressions (e.g. where && and || are both
present) should be parenthesized, so that the precedence should always be
clear.
-
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
}
Don't write embedded "simple" (without { })
conditions; only the innermost condition can be "simple" (even if there isn't
any else):
if (condition1) {
if
(condition2)
statement1;
else
statement2;
}
If there is any complex branch in the conditional
if statement then all should be complex.
The condition shouldn't contain any
assignment.
-
switch
switch (key) {
case
Value1:
// body
break;
case
Value2:
// body
// no break
case
Value3:
case
Value4:
// body
break;
case
Value5:
}
// body with local variables
}
break;
default:
// body
break;
}
It is obligatory to have a default
branch (error handling!). The default
should always be the last.
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 (long
i = 0; i < 100; i++)
statement;
-
for (long
i = 0; i < 100; i++) {
//
body
}
-
for (long
i = 0; i < 100; i++)
;
// empty body
You shouldn't write embedded "simple" (without { },
Example 1) loops, only the innermost loop can be "simple".
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 write embedded "simple" (without { },
Example 1) loops, only the innermost loop can be "simple".
You shouldn't use assignments in the condition.
-
do
statement;
while (condition);
-
do {
//
body
} while
(condition);
You shouldn't write embedded "simple" (without { },
Example 1) loops, only the innermost loop can be "simple".
You shouldn't use assignments in the condition.
-
try-catch-throw
try {
//
body
}
catch (Type1 exception) {
//
body
}
catch (Type2) {
//
body
}
catch (...) {
//
body
throw;
// rethrow
}
If it doesn't cause any problems, the recommended way of
writing the type and parameter name accepted by catch
is the following:
catch
(const Type&
exception) or
catch
(const Type&
e)
or
catch
(const Type&)
(if Type is not a basic type).
5. Variable declarations
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 (long
x, long y);
short GetVersion (void);
Also leave a space between the type and the name of the parameter.
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 (long
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
-
Implementation
-
Style:
void PrintNumber (long
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.
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 Vector
bool
isNormalized; // if it is true,
then the vector is normalized
};
Do not write the default public keyword for
structs, and do not use protected or
private sections. Write a class instead if you need such sections.
Similarly, do not use static data members and inheritance.
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 {
long x;
// x coordinate of the Point
long y;
// y coordinate of the Point
Point ();
Point (long xc,
long yc);
};
Structures can be used only in the sense you got accustomed to in C
programming. Structures may have constructors and constructor-like (Set)
methods beside the data members.
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
};
In case of value assignment align both the '=' signs and the values:
enum Token
// tokenizer constants
{
Integer = 25,
Real = 48,
Character = 701
};
Please add comments to enums describing
their role.
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
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 part
protected:
// protected part
public:
// public part
};
The opening bracket '{' of the class body should go to a new line if the
header of the class spans multiple lines (see inheritance).
In some rare cases the public (or
protected) parts contain declarations (mainly user types, e.g.
enum), which are used in the private
(or the protected) section. These declarations
should appear at the beginning of the class:
class Vector {
public:
// predefinitions
typedef double
Coord;
// other public definitions also
used by the private (protected) part
private:
// private part
protected:
// protected part
public:
// public part
};
-
Template
classes
-
Inheritance
class Derived: public
Base1,
public Base2,
protected Base3,
private Base4
{
// body
};
You should always write in case of inheritance the default private
keyword.
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
-
Body
-
private
section - typical layout, recommendations, rules
class String {
private:
//
definitions
typedef short Symbol;
enum InternalStatus { OK, Error
};
// static
(class) data members
static const
long MaxSize;
// short description
static char
defChar; // short description
// normal
(instance) data members
char*
str; // short description
long
size; // short description
// static (class) functions
static unsigned
char*
ToPascalString (const
char* cStr);
// normal
(instance) member functions (methods)
void
Clear (void);
long
SkipSign (long
from) const;
inline
long
SkipDigits (long from)
const;
virtual
void
Print (void)
const;
protected:
// protected part
public:
// public part
};
For better readability the default private keyword
should always appear at the beginning of the class.
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
If you use friend classes or functions, it should
appear at the beginning of the private section if
it needs to know about all private data and methods:
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 part
protected:
// protected part
public:
//
definitions
typedef char* CStr;
enum SymbolType { ASCII,
WideChar };
// declaration of
optional exception classes (types)
// static const
(class) data members
static 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 (long idx);
void
Delete (long from,
long range);
void
DeleteLast (void);
inline void
Clear (void);
void
Replace (long from,
long range, char ch);
inline char&
operator[] (long idx);
inline char
operator[] (long idx)
const;
virtual long
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)
unsigned long 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 long
Read (IChannel& ic);
long
ReadQuotedString (IChannel& ic, bool skipLeadingWhiteSpace
= true);
long
ReadLine (IChannel& ic);
virtual long
Write (OChannel& oc) const;
Leave at least one space between the name of the method and the opening
parenthesis of the parameter list.
-
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 (long from, long
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, long 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,
long charCount);
explicit String (unsigned
char* pStr);
String (const
String& source);
You should always put the explicit keyword in
front of the constructors which have one parameter (except the copy
constructor), unless you need automatic conversion.
-
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 always write const, except in
special cases.
-
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;
Leave a space in front of and behind the = sign.
-
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:
//
definitions
struct RealPair {
double
r1; // first value
double
r2; // second value
RealPair (): r1 (0), r2 (0) {}
RealPair (double
val1, double val2) r1 (val1), r2 (val2) {}
};
...
};
For such small utility classes you can put the method implementation into the
class.
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
9. Class implementation
-
Static (class) members
long
Array::defaultInitialCapacity = 100;
const long
Array::MaxInitialCapacity = 1000000000;
const double
Array::DefaultAllocationUnit = 100.5;
Try to align data member names, the '=' signs, and the initializer
values.
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 (long initialCapacity,
long maxSize):
base1 (initialCapacity),
base2 (0, 25),
member1 ("",
40),
member2 (1)
{
// body
}
You should align the initializers of the ancestor classes and the data members,
with the initializers of the ancestor classes coming first, and then the data
members in the order they appear in the inheritance and in the data member
declaration. Data members can also be initialized in the body if it is more
practical.
-
Style of template methods:
template <class Type>
inline long
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 NULL
if (this
!= &source) {
BaseClass1::operator=
(source);
// if Point2D has base class named BaseClass1
BaseClass2::operator=
(source);
// if Point2D has base class named BaseClass2
x = 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.
10. Namespace
namespace CLib {
void* MemCopy (void*
destPtr, const void*
srcPtr, unsigned long
count);
void* MemMove (void*
destPtr, const void*
srcPtr, unsigned long
count);
void* MemSet (void*
destPtr, int filler, unsigned
long count);
}
You should indent with one tab the body of such namespaces,
which contain only simple functions, type declarations (typedef),
and constants.
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
In this case you should add a closing comment at the end of the
namespace.
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 long
count)
{
// body
}
The compiler will give a warning if you accidentally write MemCpy instead of
MemCopy in this case, otherwise it will put the new "bad" MemCpy into the
namespace
.
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
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:
File header:
//
*****************************************************************************
// 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
}
}
You should always add comments:
-
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.
Optional (others will be thankful) if it helps in any way.
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
if the name of the header file is Location.hpp and the namespace is IO.
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.
|