Coding style - necto/denfer GitHub Wiki

Coding style is a set of rules or guidelines used when writing the source code for a computer program. It is often claimed that following a particular coding style will help programmers to read and understand source code conforming to the style, and help to avoid introducing errors.

Source base structure

To simplify project structure the following layout of source files is used.

General Rules

  • All source files should be grouped into modules/projects
  • A module/project lives in its own folder named sources/
  • Every project has the following mandatory headers:
  • dep.hpp for explicit listing of external dependencies
  • iface.hpp for external interfaces
  • impl.hpp for internal declarations Example: IR project folder is trunk/sources/ir

Namespaces

Each component must be in it's own namespace. If component consists of several sub-systems each one must be in an own nested namespace

Source files conventions

File Naming

All file names should be lowercase. Header files have extension .h for C or .hpp for C++, pure C files have .c and C++ or mixed files have .cpp. File names should reflect the functionality defined/implemented in it. File names can have prefixes for reflection of their grouping. It is preferred that we have different names for different files, i.e. files from different project can have same name technically, but this is bad from style point of view.

File Structure

A file should consist of several section desribed below. Each section should be separated by blank line. Some sections should have identifying comment. Structure:

  • Beginning comment
  • Multiple inclusion protection (for headers)
  • Includes
  • Defines
  • Local data types definitions
  • Local functions prototypes (if needed)
  • Local data
  • Externally visible functions implementation
  • Local functions implementation
  • Beginning comment

All source files MUST begin with comments that consists of:

  • File name
  • Brief Description
  • Relation to project
  • Copyright Notice
  • The long description is highly recommended to be added in comments after these. Especially for files with a lot of code or non-trivial functionality/internal structure.

Example:

/**
 * @file:graph.cpp 
 * Implementation of graph
 * Core component of Graph representation
 */
/**
 * Copyright 2013 Denfer team
 */

Multiple inclusion protection

Instead of dated #ifndef include guards, we will use #pragma once. Place it after a blank line right below the beginning comment:

/*
 * lorem ipsum
 */

#pragma once

Class description structure

Class header

Class description should be in the <class_name>.hpp file, and should have the following format:

/*
 *comment, as described in the [[Source Base Structure]] 
 */

#pragma once


// include for all types, involved in the class definition.


namespace usedNamespace
{
    prototypes of classes, mentioned below only by pointers or references.
}
...

namespace <nameOfComponent>
{

nessesary using directives

support functions prototypes

class className :parents
{
public:
    public constructors, destructor and interface methods
private:
    private members


};
}

Inline functions

If a member function has a one-line body, and that line won't break the 80-char limit, it can be (but strongly not recommended) defined inside the class description in two ways:

int fooFun (int arg1, int arg2) const { return arg1 - arg2; }
int fooFun (int arg1, int arg2) const
{ return divideTwoNumbersSafely( arg1, arg2); }

Syntax usage

Introduction

Proper usage of language syntax increases clarity in the code. It aids reading, makes developments easier and just looks better. Good formating exploits program structure by means of adjusting visual appearance of the code.

Line length

Lines of code should be not longer than 100 symbols INCLUDING comments. In addition it is highly recommended to avoid lines longer than 80 symbols. If it cannot be done - consider breaking the line and starting a new one.

Line breaks

Line breaks should try to maintain expression structure.

Example:

a = b + ( veryLongRoutineName( d + veryVeryLongNameForSomeFunc()) 
          * ( someOtherFunction() + anotherFunc( my_var, his_var) 
              - howDoYouLikeIt() * thatReallyComplex( real, imaginary)
              - youBetterFinishYourExpressionHereUseTemporaryVars()));

Indentation

Indentation unit is 4 spaces. Usage of tabs is bad style and should be avoided. Usage of tab key however can be recomended provided that a text editor is configured to substitute tabs by 4 spaces automatically.

Example:

if ( cond)
{
    for ( int i = 0; i < 10; i++)
    {
        some_var+= some_array[ i];  
    }
}

Spaces

Usage of spaces should keep structure of expressions and statements clear. Space should be inserted after:

  • Opening brace '(' if it's not followed by another brace '('. Example: a = 1 + ( ( a + 1) + ( b + 2));
  • Semicolon ';'
  • Comma ','
  • Opening bracket '['
  • Binary operators and ternary operator '? : '
  • Postfix unary operators
  • Generally, spaces should separate expressions, operators, statements, function calls. Example:
a = atoi( ( char*)( my_ptr + ( offset * sizeof( myStruct))));

However space should NOT be used in:

  • Array element, or field selection like my_struct.field; my_ptr->field; my_arr[ 10];
  • Before semicolon ';' or comma ',' like for ( a = 0, b = 0; a == b; a++, b++);

Braces

Each curly brace is placed on a new line.

Example:

/* Wrong */
if ( condition){
    return true;
}
/* Correct */
if ( condition)
{
    return true;
}

Huge blocks should have commented curly braces where possible.

Example:

if ( condition == true) /** Common case */
{
    if ( a || ( b && c))
    {
        for ( ...)
        {
            ...
        }
        if ( ...)
        {
            for ( ...)
            { 
                ...
            }
        }
    }
} /** End of common case */

Statements conventions

The statements generally inherit rules that are applied for syntax. One important exception is that function calls are written without space between function name and opening brace for parameters. For clarification purposes refer to

Examples

Examples show recommended style and point out what we'd like to avoid.

If-related statements

/* Prefer */
if ( a == b)
{
    c++;
}
/* Avoid */
if( a == b) //No space after 'if'
{
    c++;
}
/* Avoid */
if (a == b) //No space after brace
{
    c++;
}


/* Prefer */
if ( condition_one)
{
    ...
} else if ( condition_two)
{
    ...
} else
{
    ...
}
/* Avoid */
if ( condition_one)
{
    ...
} 
else if ( condition_two) // redundant line
{
    ...
} else { // BUT needed newline for this curly brace
    ...
}

Loop operators

/* Prefer */
for ( i = 0; i < MAX; i++)
{
    while ( condition)
    {
    
    } 
}
/* Prefer */
for ( i = getFirstIndex();
      !isTraversalComplete();
      i = updateIndexByTraversalStep())
{
     do
     {
         ...
     } while ( condition);
}

/* Avoid */
for ( i = 0; i < MAX; i++, a--) // Nested loops - put curly braces to ephasize
    while ( condition) a++; // Need newline here

/* Avoid */
for ( i = getFirstIndex(); !isTraversalComplete(); i = updateIndexByTraversalStep()) // too long
{
     do
     {
         ...
     } 
     while ( condition); // redundant line
}

Ternary conditional statement

/* Prefer */
a = ( b > c)? d : e;
/* Prefer */
next_element = ( current_element == null)
               ? firstElement()
               : current_element->next();
/* Avoid */
 a = ( b > c)?
      d : e; // Redundant line
/* Avoid */
next_element = (current_element == null)?getFirstElement():current_element->getNext();
// Too long and no spaces

Function calls and parameters

No space between function name and parameter list. Space after opening brace. If parameter list is empty put braces together.

/* Prefer */
newElem( curr_elem, data->temporaryDataP());
/* Avoid */
newElem (curr_elem, data->temporaryDataP( ));

Indent ending of long parameter list to align with first parameter. Alternatively you can place each parameter on separate line since it encourages commenting them.

/* Prefer */
initializeField( getFirstFieldDefault(), getSecondFieldDefault(),
                 getThirdFieldDefault(), lastParameter());
/* Prefer */
initializeField( getFirstFieldDefault(),
                 getSecondFieldDefault(),
                 getThirdFieldDefault(),
                 lastParameter());
/* Avoid */
initializeField( getFirstFieldDefault(), getSecondFieldDefault(),
                                         getThirdFieldDefault(), lastParameter());
/* Avoid */
initializeField( getFirstFieldDefault(),
                 getSecondFieldDefault(), getThirdFieldDefault(), lastParameter());
/* Avoid */
initializeField( getFirstFieldDefault(), getSecondFieldDefault(),
    getThirdFieldDefault(), lastParameter());

Naming conventions

Mostly we would like to adopt QT Style API Design and inherit naming system for it. More rules will be given here explicitly. Read Qt Documentation for now

Types and Classes

Type names should reflect entities they represents. A name should begin with an uppercase letter. To distinct parts of name use uppercase letter at the beginning of every part.

Examples: NameOfTheClass, SomeMysteriousEntity

Function names

Class methods and function names should begin with a lowercase letter and highlight parts by uppercase letters. Exception for this rule is the Constructor and Destructor for classes - they aliased to the class names.

Examples: tryNotToGetWorried();tryNotToTurnOnTo();troublesThatUpsetYou();

Not all of the source code comply with that BUT it should. It mostly applies to Get/Set routines so if you see my_object->GetSmth() feel free to turn it into my_object->smth() and change the declaration for routine.

Functions with similar meaning in different classes should have the same name.

  • Constructors - create()
  • Destructors - destroy()

Variables and Class Members

Variables and class members should be all lowercase. Parts of name should be separated by '_'.

Example:very_important_var

In order to use shorter name for getter routines the private class members should have some suffix ( like _priv or just _) after it.

Example:

class MyClass
{
    int num_priv;
public:
    inline int num() const
    {
        return num_priv;
    }
}

void f()
{
    MyClass my;
    int a = my.num();
}

Constants

All constants should be in UPPER_CASE with underscores substituting spaces.

Namespaces

All namespaces should seek one-word named, and all letters (even in acronyms) must be lowercased.

Commenting

Comments are generally more than welcome. Every routine, class member and ideally every variable should be commented. However you can omit comments in sitialtion like:

  • Access routine for private class members. One comment for group is enough.
  • If a variable name is specific enough. Example:int node_id; // Node id <--Redundant comment

In project we use both K&R C-style comments like /** / and / */ and C99/C++ comments // for comments.

Comment types

There are several comment types that should be used in project sources.

Here's example of various comments usage:

/**
 * Function description (Significant comment)
 */
void f( int my_first_parameter,   // Trailing comment
        void *my_second_parameter)// Trailing comment
{
    int counter = 0; // Trailing comment
    int acc = 0;

    /* Code comment */
    for ( counter = my_first_parameter;
          counter < MAX;
          counter++ )
    {
        acc += ( ( int *) my_second_parameter)[ counter]; 
    }
}

Significant comments

These are comments for routines, classes, files and text about algorithms put into sources. They generally can be seen as description of somewhat that is concept-level or implementation-independent. They also can be used to make specification by tool like doxygen and the code might not be available for reader. So they should be understandable without code at hand. Their syntax is:

/**
 * Class or routine description
 */
/** Enum, structure, class member or union field description */
/** One-line significant comment */

Code comments

Code comments are applied to clarify the code and imply that code is in front of the reader. In general code should be as clear that only difficult algorithms demand these comments. However if you feel your code can be somewhat hard to understand put comment with no hesitation. Or rewrite your code :) Keep it simple and comment everything that is complex by nature. Syntax:

/* Simple code comment */
// Simple code comment
/* 
 * Long and comprehensive code description, telling you what is done by strange 
 * expressions below. Maybe explaining optimizations and control structure.
 */
// 
// Long and comprehensive code description, telling you what is done by strange 
// expressions below. Maybe explaining optimizations and control structure.
//

DO NOT COMMENT CODE Do not use comment syntax to temporary disable some code execution. use #if 0 preproccessor directive instead. Exception is one-line comment in debug process. However try to use it as less as possible. Debug prints should look like:

#if DEBUG
    debugPrint( "At this point value is %u\n", value);
#endif
⚠️ **GitHub.com Fallback** ⚠️