Coding style guideline - Godlike/Unicorn GitHub Wiki

Quick links:

Goals of this document

All ideas expressed in this document are aimed to improve readability of the codebase. Since this topic is quite broad we are covering only the following aspects:

  • code markup (comments, indents, whitespace character placement)
  • naming conventions
  • design approaches

Line length

Optimal length of each line of code is 80 characters. It is not mandatory, but is recommended both for readability and to make code editor-friendly. Some developers may use portrait orientation of their monitor or utilize multiple column layout in their editor which would make word wrapping produce suboptimal result.

Try splitting your long lines to make them more readable without any word wrapping set.

Indents and whitespaces

Indents allow to distinguish different scopes of code as well as improve readability of long expressions.

  • For indents use only spaces and indent 4 spaces at a time. Set up your editor/IDE accordingly.
  • There shall be no trailing whitespace characters at the end of each line.
  • There shall be no more than 1 empty line between lines of code.
  • All scopes except namespaces shall be indented
Example of bad code
//! BAD: next line has trailing whitespaces at the end of line
int i = std::rand();    


if (0 == i) //! BAD: 2 empty lines used for separation
{
    // ...
}

namespace unicorn
{
    namespace video //! BAD: extra indent
    {
        class Renderer; //! BAD: extra indents
    }
}
Example of good code
//! GOOD: no trailing whitespaces
int i = std::rand();

if (0 == i) //! GOOD: 1 empty line used for separation
{
    // ...
}

namespace unicorn
{
namespace video //! GOOD: no extra indents
{

class Renderer; //! GOOD: no extra indents

}
}

Long expression splitting

  • Long expressions shall be split into multiple lines that have 1 extra level of indentation. Only comments are allowed to match indentation.
  • If there are commas or logic operators, they shall be put on the next line to allow for easier diff calculations when these expressions are expanded. (*)
  • Closing scopes shall be put on their own line (*)

(*) - may be OK where it doesn't hurt readability

Example of bad code
if (firstCheck != 42 && //! BAD: logic operator on the wrong line
    secondCheck == 42)  //! OK: closing brace is not on its own line
{
    someFunction(someArgument,
                 someOtherArgument, //! OK: commas on the wrong lines
                 yetAnotherArgument
                 ); //! BAD: matching indent
}

Window::Window(uint32_t id, int32_t width, int32_t height,  //! OK: arguments are lightly split
    const std::string& name, Monitor* pMonitor, Window* pSharedWindow) :
    m_id( id ), m_size( width, height ), m_position( 0, 0 ),
    m_name( name ), m_pMonitor( pMonitor ), m_pSharedWindow( pSharedWindow ), m_handle( nullptr ) //! BAD: initializer list is not split
{
    // ...
}
Example of good code
if (firstCheck != 42
    && secondCheck == 42    //! GOOD: logic operator on the right line
    )   //! OK: closing brace on its own line on extra indent level
{
    someFunction(someArgument
        , someOtherArgument
        , yetAnotherArgument    //! GOOD: commas on the right lines
    );  //! OK: closing brace on its own line and on original indent level
}

Window::Window(uint32_t id, int32_t width, int32_t height,  //! OK: arguments are lightly split
    const std::string& name, Monitor* pMonitor, Window* pSharedWindow)
    : m_id( id )
    , m_size( width, height )
    , m_position( 0, 0 )
    , m_name( name )
    , m_pMonitor( pMonitor )
    , m_pSharedWindow( pSharedWindow )
    , m_handle( nullptr )   //! GOOD: initializer list is split, colon and commas are on the right lines
{
    // ...
}

Comments

There are 3 types of comments that should be thought of:

  • class comments describing classes, their methods and variables
  • code comments describing complex algorithms and nonintuitive pieces of code
  • copyright comment blocks

Comments shall be descriptive of what is the purpose of X or what X does, but not what X is, how X is used or any other implementation-related information. When writing Doxygen comments use //! for single line comments and /** ... */ for multiline comment blocks. Try matching asterisk characters in multiline comment blocks as follows:

Example code
/** @brief  Brief description
 *
 *  @param  arg0    first argument
 *  @param  arg1    second argument
 */

When writing general comments use // for single line comments and /* ... */ for multiline comment blocks. Matching indent is optional. Remember that main idea is improving code readability so if something is not hard to do and results in easier-to-read code - do it. Argument descriptions shall contain information on what it represents and possible constraints or details of the argument. As a general rule do not mention implementation details when describing arguments.

Class comments

To produce documentation we use Doxygen and all files in header directories are used as input for it.

Class comments should utilize Doxygen commands in @-format. When writing Doxygen comments keep it mind that they are used by Doxygen and developers alike so readability is a pretty big factor.

To improve readability of Doxygen comment blocks use empty lines to separate blocks and indent to align/markup information (mind the indent columns, try not to indent manually). Notice that in the following example there are consecutive whitespace characters after special Doxygen commands, this is done intentionally for markup purposes.

Example code
/** @brief  Does magical things
 *
 *  Magical things include but are not limited to:
 *  - cure diseases
 *  - mend wounds
 *  - create rainbow
 *
 *  @attention  Use this method at your own risk
 *
 *  @note   Magical things are of unknown nature and may not be available
 *          in all regions/environments
 *
 *  @param  seed            random seed for magical things
 *  @param  onlyPositive    flag describing if only positive powers shall be
 *                          applied
 *
 *  @return @c true on success, @c false otherwise
 */
bool UnicornPower(uint64_t seed, bool onlyPositive);

//! Window title name
std::string m_name;

//! Pointer to a monitor associated with the window
Monitor* m_pMonitor;

Code comments

Even though the general rule is code should be self-explanatory sometimes you should add comments inside your code to guarantee readability (because of complex algorithms or nonintuitive pieces of code).

It is recommended to match preprocessor #endif with a comment describing what the preprocessor check was about.

It is recommended to match namespace closing brace with a comment containing namespace's name.

Example code
#ifdef UnicornEngine_EXPORTS
#define UNICORN_EXPORT __declspec(dllexport)
#else
#define UNICORN_EXPORT __declspec(dllimport)
#endif  // UnicornEngine_EXPORTS

namespace unicorn
{
// ...
} // unicorn

Copyright blocks

Each source file should start with appropriate copyright information. For our code (not third party) the following snippet should be used:

  • C++
/*
* Copyright (C) 2017 by Godlike
* This code is licensed under the MIT license (MIT)
* (http://opensource.org/licenses/MIT)
*/
  • CMake
# Copyright (C) 2017 by Godlike
# This code is licensed under the MIT license (MIT)
# (http://opensource.org/licenses/MIT)
⚠️ **GitHub.com Fallback** ⚠️