Coding Conventions

This section provides a summary of what is considered good coding style in Calitko Project. We urge you learn and follow these style guidelines! Some of them are mostly esthetic in nature. Some aim to save us the trouble to write the same thing over and over again. Others would either make it easier to make changes, or are simply good practices, which can reduce the possibility for bugs. Let us know if you have any suggestions or comments!

All source code you submit to Calitko should be conforming to the guidelines described here. We will most probably defer accepting a patch that fails to do so.

Code Organization

Packages (namespace)

A package in Calitko's architecture groups a number of component and data object types, which are related or belong to the same application domain. Each package uses a directory with the same name. All source and header files are located in this directory. A package can contain a number of sub-packages, each in its own sub-directory. Some (bigger) packages may be compiled as separate libraries or plug-ins.

Package Documentation (PackageName.cpp)

A package may have (bigger packages containing sub-packages must have) a source file that contains the package documentation. This documentation should provide a high level overview of the purpose of the package, as well as a summary of how the package is organized. It should be said which are the most important sub-packages and components and how they are related to each other. The file must have the same name as the package it is describing and it must have a .cpp extension in order to be parsed by doxygen. A typical file looks like this:

// License note...

/*! \namespace PackageName
	\brief Brief description of the package.

	An overview...
*/

If this file happens to be the first and only file in the package, than add adeclaration of the namespace, so that doxygen does not discard the documentation when it finds no declaration of the package's namespace. After the namespace is declared elsewhere this declaration can be deleted:

// \todo Remove when declared elsewhere:
namespace PackageName {
};

Imports

A package can import and use component and data object types provided by other packages. Imports provide an overview of the dependencies to other packages. Additionally, it saves you the trouble to write a bunch of #includes and using statements in the beginning of each file. All you have to do in a header file is:

// Package/MyClass.h
/* License note...
*/
#include "Imports.h"

// ...

Or in a source file:

// Package/MyClass.cpp
/* License note...
*/
#include "Qt.h" // <-- precompiled header
#include "MyClass.h"
// further includes from the *same* package
#include "Imports.cpp" // to include external type definitions

//...

Now let's describe the difference between Imports.h and Imports.cpp.

Imports.h

This file will import all types needed by any header file from the package. Include the header files declaring types, or forward-declare classes/structs, from other packages. Import all required names in the package namespace to avoid explicitly specifying them. Some rules:

Protect the file against being included multiple times using the C preprocessor. Derive the #define from the file name relative to project's root. Note that "__" is used as directory separator and "_" as word separator.

#ifndef PACKAGE__SUB_PACKAGE__IMPORTS_H
#define PACKAGE__SUB_PACKAGE__IMPORTS_H

Use forward declarations instead of including a header whenever possible to speed-up the compilation process! Either use the classical way for global names (e.g. class MyClass), or use the macro FORWARD_DECLARE (NamespaceName, class ClassName) or one of its FORWARD_DECLAREX versions. The X is the number of namespaces the type is in.

FORWARD_DECLARE (OtherPackage, class ClassA)
FORWARD_DECLARE2 (OtherPackage, SomeSubPackage, class OtherClass)

Include the header files for all types for which you cannot use a forward declaration! You would need to do that if you declare an object of this type in one of the package header files. The same is if you pass an object of this type by value in a function.

#include "OtherPackage/SomeSubPackage/OtherStruct.h"

Import all names from other packages (using the using statement) in the namespace of this package.

namespace Package {
namespace SubPackage {

    using   OtherPackage::ClassA;
    using   OtherPackage::SomeSubPackage::OtherClass;
    using   OtherPackage::SomeSubPackage::OtherStruct;

}; // namespace SubPackage;
}; // namespace Package;

Finally close the including guard:

#endif // PACKAGE__SUB_PACKAGE_IMPORTS_H
Imports.cpp

Include the complete declarations of all types from other packages that are used in source files or were incompletely declared in Imports.h. Again some general rules:

Include the header files for all types which were only forward-declared in Imports.h.

#include "OtherPackage/ClassA.h"
#include "OtherPackage/SomeSubPackage/OtherClass.h"

Include all header files that are only needed in source (.cpp) files.

#include "YetAnotherPackage/ClassB.h"

Finally import the name of the classes that are used only in the source files. No need to put the using statements in a namespace now. If you want to import two classes with the same name from different packages, that won't work. Import only one, or none; and write the using statements before using the name or you would need to fully qualify it.

using YetAnotherPackage::ClassB;
Namespace.h (obsolete)

Note: This file is obsolete! Stop using it and refactor existing code! This file contains forward declarations of the classes declared in the package.

Component Types (class)

A component type is implemented as a class. It is more than an object data type which represents some data abstraction and encapsulates a number of operations on this data. It is characteristic for a component type that it has behavior of its own. It reacts on stimulus from its outside and in effect can send stimulus to other components, thus affecting the control flow of the system, or modifying its state.

We implement component types using classes in C++. For each class we declare a header and a source file with the same base name as the class name (e.g. Component.h and Component.cpp). Below are some rules and examples for each of these files:

Sample Component.h

#ifndef MY_PACKAGE__COMPONENT_H
#define MY_PACKAGE__COMPONENT_H

#include "Imports.h"

namespace MyPackage {

class ComponentPrivate;

class Component
{
    Q_OBJECT

public:
            Component();
            ~Component();

    int     width() const;
    void    setWidth (int);

private:
    ComponentPrivate *p;
};

}; // namespace MyPackage;

#endif // MY_PACKAGE__COMPONENT_H

Sample Component.cpp

#include "Imports.cpp"
#include "Component.h"

namespace MyPackage {

enum Constants {
    MyTimeout       = 10000,
};

class ComponentPrivate
{
public:

    int     width;
};

}; // namespace MyPackage;

using namespace MyPackage;

int Component::width() const
{ return p->width; }

void Component::setWidth (int width)
{ p->width = width; }

Component::Component()
{
    p           = new ComponentPrivate;
    p->width    = 0;
}

Component::~Component()
{
    delete p;
}

Data Object Types (class, struct, typedef, enum)

In contrast to component types, data object types can only change their internal state as result to an operation invoked on them. They are not allowed to modify or affect any other part of the system than themselves. Data Object types should know nothing of the context in which they are used. They just encapsulate data and operations on this data.

There are different ways to declare a new data object type:

Complex Data Object Type (class)

A complex data object type is very similar to a component in its implementation. Here are the important differences:

Sample MyClass.h

#ifndef MY_PACKAGE__MY_CLASS_H
#define MY_PACKAGE__MY_CLASS_H

#include "Imports.h"

namespace MyPackage {

class MyClass
{
public:
            MyClass();
            ~MyClass();

    int     value() const;
    void    setValue (int);

private:
    struct Data
    {
        int value;
    };

    Data    d;
};

inline int MyClass::value() const
{ return d.value; }

inline void MyClass::setValue (int value)
{ d.value = value; }

}; // namespace MyPackage;

#endif // MY_PACKAGE__MY_CLASS

Sample MyClass.cpp

#include "Imports.cpp"
#include "MyClass.h"

using namespace MyPackage;

MyClass::MyClass()
{
    d.value = 0;
}

MyClass::~MyClass()
{
}

Simple Data Object Type (struct)

Sample MyStruct.h

#ifndef MY_PACKAGE__MY_STRUCT_H
#define MY_PACKAGE__MY_STRUCT_H

#include "Imports.h"

namespace MyPackage {

struct MyStruct
{
    int             myInt;
    float           myFloat;
    ImportedType    myImportedType;

    MyStruct();
};

}; // namespace MyPackage

#endif // MY_PACKAGE__MY_STRUCT_H

Sample MyStruct.cpp

#include "Imports.cpp"
#include "MyStruct.h"

using namespace MyPackage;

MyStruct::MyStruct()
{
    myInt   = 0;
    myFloat = 0.;
}

Enumeration Object Type (enum)

enum PayloadDescriptor
{
    PingDescriptor              = 0x00,	//!< Ping packet
    PongDescriptor              = 0x01, //!< Pong packet
    QueryDescriptor             = 0x80, //!< Query packet
    QueryHitsDescriptor         = 0x81, //!< Query hits packet
    PushDescriptor              = 0x40, //!< Push packet
    // Extension Decriptiors:
    ByeDescriptor               = 0x02, //!< Bye packet
    IbmcDescriptor              = 0x10, //!< In-band control message
    QueryRoutingDescriptor      = 0x30, //!< Query routing protocol packet
    OpenVendorDescriptor        = 0x31, //!< Open vendor packet
    StandardVendorDescriptor    = 0x32, //!< Standard vendor packet
};

Data Object Type Alias (typedef)

There is nothing special about typedefs except aligning the type names if there are multiple definitions on subsequent lines:

typedef QList <NodeAddress>             NodeSet;
typedef QMap <NodeAddress, NodeInfo>    NodeInfos;

Doxygen Documentation

All source code must be documented using Doxygen tags in Qt style (that is use ! and not @ to tag comments, the latter is known as Javadoc style). All documentation should be written right before the declaration that is being documented. Always write brief and detailed descriptions.

A typical class documented would look like this:

//! Brief description of MyClass here.
/*!
    More detailed description here...

    \note Some note regarding this class.
    \todo Some TODOs here.
*/
class MyClass : public QObject
{
    Q_OBJECT

public:
            MyClass();
            ~MyClass();

    void     foo();
};

A typical (member) function should be documented like this:

//! Brief function description here.
/*!
    More detailed description here...
*/
void MyClass::foo()
{
    ...
    //! Inline doxygen comment.
    ...
}

Package (namespace) documentation is written in a special file, which has the same name as the namespace it is describing and is located in the package directory. A typical namespace documentation looks like this:

/*! \namespace Package
    \brief Brief package description.

    Detailed package description...
*/

A typical enum documentation would look like this (note how enum values are described following the detailed enum description):

//! Brief enum description.
/*!
    Detailed description here...

    \value MyEnumValueA
    \value MyEnumValueB
*/
enum MyEnum
{
    MyEnumValueA,
    MyEnumValueB,
};

CppUnit Tests

TODO! Calitko unit tests are based on the CppUnit framework.

Memory Management

This section recommends some practices, which would reduce the risks of and simplify issues related to managing dynamically allocated memory. Clearly, the best way to avoid such problems is not to do it! This means that whenever possible one should prefer declaring objects on that stack instead of on the heap. If you do so, then the compiler takes care of destroying the objects and freeing the associated memory.

Implicitly shared private data

It is recommended that custom types implement implicit sharing by means of reference pointers. Doing so would reduce the cost of copying and assigning objects of custom types. These operations would be needed more often as values instead of pointers are copied to and from the stack. Qt4 provides the class QSharedData, which can be used together with QSharedDataPointer to implement reference counting on private data.

Passing objects to functions

Arguments of generic types should be passed by value, unless they are designed to be used as output arguments. Arguments of user defined types should be passed by constant reference, unless designed to be used as output arguments, in which case non-const references should be used. Avoid passing pointers to objects in calls to functions. Not passing pointers to objects saves us some thinking about who is the owner of the object. If the idea is to pass object ownership to the function, than use an auto_ptr argument. In rare cases when one object should be bound to another one (e.g. binding a QDataStream to QDevice *), a 'normal' pointer can be used.

Returning values from functions

Objects of non polymorphic types should be returned by value. The cost of copying the object will be small if implicit sharing is used. For functions that return objects of polymorphic types through pointer to the base class should return and auto_ptr for the base class instead. Using auto_ptr automatically passes ownership of the heap-allocated object to the caller. If the auto_ptr goes out of scope and it still owns the object, the object will be deleted. If the caller fails to assign the return value, the object will be deleted as the temporary auto_ptr gets destroyed.

Member and non member pointer variables

An auto_ptr (?? TODO: use boost::scoped_ptr instead ??) should be used instead of a plain C pointer. Using auto_ptr guarantees that the object will be deleted when the auto_ptr goes out of scope and thus exception safety is increased.

Objects as arguments in signals and slots

Object of non polymorphic types should be passed by value. Copying will be efficient if reference counting on the private data is implemented. Objects of polymorphic types that are referenced with (non) const references/pointers to the base class should be emitted in signals only using smart pointers (?? TODO: maybe boost::shared_ptr ??). In this case auto_ptr is not suitable since there may exist multiple slots connected to the same signal. That would result in copying the auto_ptr multiple times and at most one of the slots will get an auto_ptr that still owns the object.

Coding Style Guidelines

The guidelines presented in this section are mostly based on the C++ Programming Style Guidelines of Geosoft.

Names

Avoid abbreviating names. Using full names makes the code less ambiguous and self-documenting.

Type names must be written in Pascal case:

namespace MyNamespace {};
class     MyClass;
struct    MyStruct;
typedef   QList  IdsList;

Names of constants (enumeration values and static const variables) must be written in Pascal case:

enum DayOfWeek
{
    Monday,
    Tueseday,
    ...
}
static const int MaxCount = 100;

Variable names and function names must be written in camel case:

int       accountNumber;
MyClass   myClass;
MyStruct  myStruct;
IdsList   ids;
int       turnRight();
bool      parse();

Names of macros must be all upper case. Note however that you should prefer enum values or static const variables:

#define FORWARD_DECLARE(ns, def) namespace ns { def; };
#define MAX_COUNT 100

Template type names should be a single capital letter. The types should be declared using typename instead using class because you might instantiate with int and it is not a class:

template <typename T> ...

Abbreviations and acronyms should not be uppercase when used in a name:

HtmlHeader htmlHeader;
parseHttpResponse(httpResponseBytes);
setId(myId);

Global names should be specified using the global scope operator. Note that using global objects or functions should be avoided:

::registerListener (this);

When declaring a variable of a given type, use the type name if the variable is generic or combine the type name with the specific role the variable will be used in:

void print (String *string);
print (titleString);
Widget *childWidget = new Widget (parentWidget);

You should prefer nouns for type names:

class Widget;
class Packet;

You should prefer verbs for function names:

void calculateBalance();
execute();

You could prefix the names of bool functions with is, has and can:

isFull();
hasPriority();
canRead();

You could prefix the names of functions that perform transformations with to:

toHexString();
toInt();

Use plural forms for collections:

typedef QSet  Sessions;
Sessions sessions;

Do not use prefixes specifying type of variable:

int    length;  // NOT int    nLength;
bool   done;    // NOT bool   bDone;
Widget *widget; // NOT Widget *pWidget;

Statements

Types and declarations local to a source file should be declared in the source file (e.g MyClassPrivate classes).

Class declarations should be ordered by public, protected, private.

Make explicit type casts, even when implicit casting would work:

int   intValue   = static_cast  (floatValue);
float floatValue = static_cast  (intValue);

Variables must be initialized when declared. Variables should be declared right before using them, if possible.

bool isDone = false;
while (!isDone) {
    // ...
}

Try to avoid global variables. Use static if only local to a file.

Public class variables should be avoided.

Prefer explicit tests for zero for non-boolean types:

int value;
// ...
if (value == 0) { ... }
if (value != 0) { ... }

A *a;
// ...
if (a == 0) { ... }
if (a != 0) { ... }

bool condition;
// ...
if (condition)  { ... }
if (!condition) { ... }

Try keeping variables in the smallest scope possible.

Prefer while (true) {...} to other possible ways to write an infinite loop.

If a conditional expression is too long and complex, prefer splitting it in parts and assigning the partial expression to temporary variables.

bool isReady = ...;
bool hasMoreOnQueue = ...;
if (isReady and hasMoreOnQueue) {
    // ...
}

Use 0, not NULL for pointer.

General Layout

Use a tab size of 4 for indentation.

Each line in a source or header file should have a maximal length of 80 characters.

Long lines should be broke after a comma or an operator. The new line should be aligned with the beginning of the expression.

Blocks and Block Indentation

A namespace declaration should look like this:

namespace MyNamespace {

// Declarations not indented!

}; // namespace MyNamespace;

A class declaration should look like this:

class MyClass : public MyBase
{
    // ...
};

A struct declaration should look like this:

struct MyStruct
{
    // ...
};

An enum declaration should look like this:

enum MyEnum
{
    // ...
};

A function definition should look like this:

int main (int, char *[])
{
    // ...
};

An if, else if, else statement should look like this:

if (condition) {
    // ...
} else if (condition2) {
    // ...
} else {
    // ...
}

The semicolon of an empty statement should be written on the next line:

if (...)
    ;

A for statement should look like this:

for (initialization; condition; step) {
	// ...
}

A while statement should look like this:

while (...) {
    // ...
}

A do while() statement should look like this:

do {
    // ...
} while (...);

A switch statement should look like this:

switch (...)
{
case 1:
    // ...
    break;
case 2:
    {
        // ...
    }
	break;
default:
    break;
}

A try, catch statement should look like this:

try {
    // ...
} catch (Exception &e) {
    // ...
} catch (...) {
    // ...
}

Whitespace

Operators should be surrounded by a space.

C++ reserved words should be followed by a space.

Commas should be followed by a space.

Colons and semicolons should be followed by a space.

Colons in a class declaration should be surrounded by a space.

The names of functions with non-empty argument list should be followed by a space to increase name readability.

void myFuntion (bool with, int arguments);

The names of functions with empty argument list should not have whitespace between them and the parentheses.

void myFuntion();

The name of an array should be followed by a space if the index is non-empty:

array [index] = value;

The name of an array should not be followed by a space if the index is empty:

char string[] = "Some string.";

Template argument lists should by surrounded by a space:

QList 	values;
template  ...;

Use a blank like to logically separate different parts of the code.

A single blank line is enough to separate function definitions.

Use alignment wherever it enhances readability. For example:

if (a > b)          value = a;
else if (b > c)     value = b;
else if (c > d)     value = c;
else                value = d;

class MyClass
{
public:
            MyClass();
            ~MyClass();

    void    setValue (int value);
    int     value() const;
};

void foo()
{
    int    height = 10;
    int    width =  20;
    float  ratio =  0.4;

	// ...
}

Pointer or reference symbol should be put next to the name.