Coding Style

Code written for Pedigree should adhere to the style rules outlined below.

File organisation

  • All C++ source files should end in either .h or .cc , NOT .cpp.
  • All global functions which are non-trivial should have their own .cc file where able. Multiple non-trivial functions may be implemented in one file if they are directly related to each other and rarely used outside of the file itself.
  • The declarations for multiple related global functions can and should be collated in one .h file.

Naming convention

  • Classes shall be named in UpperCamelCase. Each class should have at the least its own .h file, and unless it is abstract or for some reason has limited functionality, its own .cc file. Some exceptions can be made, but these are definitely the exception to the rule.
  • All functions shall be named in lowerCamelCase, and should be explanatory of the purpose of the function if possible.
  • All variables shall be named in lowerCamelCase. The name of a variable of array type shall denote that it consists of more than one object of that type. Variable name should be prefixed if appropriate - the following prefixes are defined (all other prefixes are prohibited):
    • m_ for private member variables
    • g_ for global variables
    • p for pointer variables (including types that decay to a pointer type, e.g. arrays)
    • b for boolean variables
    • n for integer variables used as a counter
    • s for string variables
  • Macros shall be named in uppercase, as should any preprocessor flags.

Syntax style

  • Code shall be indented with 4 spaces
  • Braces shall be placed on their own line, not indented any further from the previous line, e.g.
    if(foo == bar)
    {
        doSomething(bar);
    }
  • Do not place a space between function names and the bracket when making a function call - same goes for if, while, for, etc. e.g.
    if(foo == bar)
        doSomething(param);
  • In switch statements, the case should be indented from the switch, and the code within the case should be further indented, e.g.
    switch(foo)
    {
        case 1:
            doSomething(foo);
            break;
    }
  • In calculations, there should be spaces around each operator, and in more complex calculations brackets should be used to make the code easier to understand, e.g.
    m_pData[x + (m_CursorY * m_nWidth)]
    rather than
    m_pData[x+m_CursorY*m_nWidth]
    . Brackets should also be used to enforce order of operations explicitly.
  • When using pointers/references, the */& should be positioned with a space in between the type and the */&, e.g.
    MyClass *pFoo
    rather than
    MyClass* pFoo
    • The exception to this rule is in template arguments, eg SomeTemplate<PointerType*> rather than SomeTemplate<PointerType *>
  • Variables should be defined at the start of scope where possible.
  • Non-documentation comments (see below) should be written using //
    • /* */ should not be used even for multi-line comments outside of documentation

Code documentation

File documentation

Each source file should have the License header at the top, followed by a Doxygen addtogroup comment, followed by a comment with a brief description of the file's contents. The last of these can also include a longer description, a list of things useful as a reference when working on the code and a list of to-dos for everything in the file. For example:

/* License header here */

/** @addtogroup kernelmem
 * @{ */

/**
 * @file
 * @brief     Slab-style memory allocator.
 *
 * Reference:
 * - The slab allocator: An object-caching kernel memory allocator
 *   http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.29.4759
 * - Magazines and Vmem: Extending the Slab Allocator to Many CPUs and
 *   Arbitrary Resources
 *   http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.6.8388
 *
 * This implementation uses the magazine layer described in the second of the
 * above papers for good multiprocessor scalability. The only difference is
 * that we do not leave empty slabs lying around - when a slab becomes empty,
 * it is freed immediately.
 *
 * @todo      Dynamic magazine resizing.
 * @todo      Allocation hash table resizing.
 */

/* File contents here */

/** @} */

Or if there is no extra description/todos:

/* License header here */

/** @addtogroup kernelmem
 * @{ */

/**
 * @file
 * @brief Slab-style memory allocator.
 */

/* File contents here */

/** @} */

Variable documentation

Within a file, global variables should be documented with a single line, e.g.:

/** Variable that does something. */
MyClass foo;

Function documentation

Functions should be documented in two ways. Global/public functions should have a brief comment, a longer comment that explains how to use the function, etc, a parameter list and a return value, for example:

/** Do absolutely nothing to some numbers.
 *
 * Does absolutely nothing to the two numbers that it is given.
 *
 * @param num1         First number.
 * @param num2         Second number.
 *
 * @return             The first number.
 */
int MyClass::doNothing(int num1, int num2)
{
    return num1;
}

Static/private functions need only have a short description, parameter list and return value, like so:

/** Add two numbers together.
 * @param num1         First number.
 * @param num2         Second number.
 * @return             The result of adding the two numbers. */
int MyClass::doSomething(int num1, int num2)
{
    return num1 + num2;
}

Casting

  • Never use C-style casts unless you are writing C
  • For most uses, static_cast should suffice. static_cast will convert types properly but it will also perform correct traversal of an inheritance tree where necessary.
  • reinterpret_cast should only be used for moving between pointer and integer types, or pointer types of the same class. It should NEVER be used to cast between different levels of inheritance.
    • One exception is where a class needs to be passed as a void* parameter. In this case, static_cast to the lowest possible base class (eg, Disk in the case of disks) that the destination requires (eg, the partitioner might work with Disk objects, not FileDisk objects) and THEN use a reinterpret_cast to convert to void*
  • When performing casts that work with a virtual base class (eg, converting "this" to a Device object in the case of a device driver), use dynamic_cast
    • dynamic_cast returns FALSE upon failure, so do NOT assume the pointer you get is valid

In summary, static_cast often, rarely use reinterpret_cast, and dynamic_cast is necessary for casts around virtual base classes.

Also available in: HTML TXT