Pichat Coding Standard for C++
Last update 04/Jun/2008 by Mark Seuffert
This document lists guidelines for writing C++ code. The goal of a coding standard
is to create code that looks like it was written from one person. Every developer
must be able to quickly dive into different parts of the source code in order to
read, understand, maintain and improve it. Saying that, this document intends to
make life easier by defining a short but clear set of rules.
Use braces, white space and comments as following:
if(expression) statement;
if(expression)
{
statements;
} else {
statements;
}
for(expression; expression; expression)
{
statements;
}
while(expression) statement;
while(expression)
{
statements;
}
do
{
statements;
} while(expression);
switch(expression)
{
case constant:
statements;
break;
default:
statements;
}
expression ? statement : statement;
// Comments are using C++ syntax only
// ... even for multiple lines, it's ugly to mix commenting styles
Notes:
- opening/closing curly braces are typically on the same level
- indentation is done via ONE tab, not two or three or four spaces
- grouping within expressions/statements is done with spaces
- one statement per line, long statements can be broken into multiple lines
- lines should not exceed a length of 100 characters
Give classes and methods an as long name as necessary, avoid abbreviations. Name
them after what they do, if you can't find a non-ambiguous name you haven't
thought through your design well enough.
Use braces, white space and name identifiers as following:
class CDataContainer : public IDataInterface
{
public:
CDataContainer();
virtual ~CDataContainer();
enum { DATA_NONE = 0,
DATA_ON,
DATA_OFF,
};
enum { SINGLE_LINE_ID = 0 };
struct DataEntry
{
type name1;
type name2;
};
void ResetDefaults();
type GetValue() const;
void SetValue(type name1, type name2, type name3 = value);
protected:
virtual void OnOpen();
virtual void OnClose();
void Update();
type CheckConsistency();
private:
type m_memberVariable;
static type s_staticVariable;
}
namespace DataNamespace
{
definitions;
}
const type DATA_MAX_LENGTH = value;
static type CClassName::s_staticVariable;
type g_globalVariable;
type CDataContainer::CheckConsistency()
{
asserts;
statements;
return value;
}
Notes:
- classes are capitalised and start with an upper case 'C', e.g. CDataContainer
- interfaces are capitalised and start with an upper case 'I', e.g. IDataInterface
- method names are capitalised, e.g. ResetDefaults()
- namespaces, structs and types are capitalised, e.g. DataNamespace
- constants and enum labels are all upper case with word separator, e.g. DATA_NONE
- arguments are separated by space, e.g. SetValue(1, 2, 3);
- identifiers use English letters and numbers only
- destructors are per default public and virtual
- methods are public or protected, member variables are per default private
- curly braces and indentation are done in the same way as in control structures
Give variables an as short name as possible, avoid abbreviations and one character
variables. From looking on a variable name you must instantly understand what the
variable is used for.
Use variable prefixes as in the following examples:
int nIndex;
bool bFound;
CString8 sOutput;
const char* szServerName;
const BYTE* pData;
const BYTE** ppMemory;
CPtrOwned<BYTE> pData;
CPtrShared<BYTE> pData;
BYTE data[DATA_MAX_LENGTH];
std::vector<char> buffer;
List of available prefixes:
| b | = bool | | f | = float | n | = int (signed/unsigned) |
| by | = byte | | h | = handle | s | = string object |
| c | = char | | it | = iterator | sz | = zero terminated string |
| dw | = dword | | l | = long (depreceated) | w | = word |
| m_ | = member variable, in front of any other prefix |
| s_ | = static variable, in front of any other prefix |
| g_ | = global variable, in front of any other prefix |
| p | = pointer or smart pointer, one prefix per indirection |
Notes:
- variables begin with a lower case letter, e.g. bFound, nIndex, file, socket
- variables use prefixes for known types only (see above), don't invent new ones
- arrays and data containers (STL) don't use a prefix for their element type
- member/static/global variables have one extra prefix separated by an underscore,
e.g. m_memberVariable, g_globalVariable
- variable modifiers/types are part of the type (not the name), e.g. int* or int&
- variable identifiers use English letters and numbers only
- local variables are declared when needed, not at the top of a method/block
- when working with arrays use the sizeof() operator for the number of bytes and
DIMENSIONOF() for the number of array elements
Use asserts to define assumptions and pre-conditions. Think of it as a contract,
your code will work properly within those limitations and you will be notified when
a condition is not fulfilled any more.
What to check in asserts:
- arguments that will be used and expected to be in a specific range (in same method)
- pointers that will be dereferenced (in same method)
- arrays that will be accessed and where the index could be out of range (in same method)
- assumptions that you depend on, especially critical ones to detect malfunctions
What not to check in asserts:
- arguments and pointers that will only be forwarded to other methods
- anything that you will check again in an if() control block in same method
Notes:
- pre-conditions are placed at the beginning of a method (for easy maintenance)
- first write and improve code, then update asserts in all affected methods
- ASSERT() is evaluated in debug builds only, there is no overhead in release builds
- ASSERT_STATIC() is evaluated at compile time, it has no overhead but it's limited
to constant expressions
- don't use asserts as a replacements for safe programming, e.g. unfiltered input
from users must still be handled with great care
Make sure that ownership and lifetime of objects are easy to understand. It must be
crystal clear about who is responsible for destroying an object and when this happens,
both is equally important in C++ code.
- use data containers (STL or CDataBuffer) when ever possible, avoid manual memory
allocations with new and delete
- alternatively rely on RAII (Resource Acquisition Is Initialization) that memory is
automatically deallocated when an object goes out of scope, e.g. use smart pointers
such as CPtrOwned or CPtrShared
- pointers in classes and structs must clearly show if they own an object, mark these
members with an "owned" comment. Alternatively use smart pointers such as CPtrOwned
or CPtrShared which automatically take care of ownership
- understand the law of the Big Two, classes that handle memory/resources usually
need a copy constructor and an assignment operator. Alternatively derive a class
from CNonCopyable in order to prevent shallow copying completely
- pointers must be assigned to NULL or the address of a valid object at any time
- don't guess the size of a memory block when reading or writing. Never ever.
- avoid unnecessary data copying, e.g. provide buffers big enough to hold results
C++ source files use the suffix .cpp
C++ header files use the suffix .h
- header files contain a header guard to prevent multiple inclusions
- file names use English letters, numbers and underscore only
- directory names use English letters, numbers and minus only
- include only header files that are absolutely necessary to compile
Apply the following rules to your code. In case you are not familiar with technical
items or concepts mentioned in this document please consult a software specialist
or a good book (see appendix). C++ is a language that gives a great deal of control
and freedom to the developer, but this comes with the price of responsibility to
use the language safely.
- first rule of coding standards is do not argue about coding standards! There are
many styles and habits, the basic point of a standard is that we have chosen ONE.
- an untested line of code is a buggy line of code until the opposite has been proven
- complex code is often problematic code, break down problems into smaller chunks
- neglected design is expensive design, focus on maintainability and understandability
- always initialise simple member variables in the constructor. Always, every time.
This means all fundamental/primitive types and pointers (e.g. bool, char, int), but
not class objects such as CDataBuffer or CFileStream
- use const keyword when ever possible, check that methods/arguments are const correct
- use references when ever possible, pointers when necessary
- comments inside methods are often a sign for refactoring, better write good code
that uses self-explaining variable/method names, mark issues with a "TODO:" comment
so that they can be found again and fixed later
- comments outside methods are a very welcome addition, e.g. for documentation of
classes, methods and member variables
- when necessary write down design documents to explain algorithms and protocols
- don't spend much time on things that will be thrown away or definitely changed later
- don't remove debugging output after testing, integrate it e.g. with TRACE
- identifiers and comments are written in English language, European/British spelling
- strictly no hard-coded values or magic numbers, instead use constants and enums
- strictly no macros or gotos, there are better alternatives in the C++ language
- strictly do not pollute global namespace, place everything in namespaces/classes
- strictly no duplicated code (no copy/paste actions), instead share functionality
- when in doubt aim for consistency, apply the same patterns all over your code
- external code (e.g. libraries) will not be converted to this coding standard
- exceptions to this standard can be negotiated on a per case basis
Further readings on the C++ language, design and best practices. All of them are
worth to have on your desk or at least in a book shelf not far away from you. :)
- Koenig/Moo "Accelerated C++"
- Bruce Eckel "Thinking In C++"
- Nicolai Josuttis "The C++ Standard Library: A Tutorial and Reference"
- Scott Meyers "Effective C++"
- Scott Meyers "Effective STL"
- Herb Sutter "Exceptional C++"
- Herb Sutter "Exceptional C++ Style"
- Matthew Wilson "Imperfect C++: Practical Solutions for Real-Life Programming"
- Robert Martin "Agile Software Development: Principles, Patterns and Practices"
- Martin Fowler (et al) "Refactoring: Improving the Design of Existing Code"
For further information and feedback please contact me.