Coding style guideline - Godlike/Unicorn GitHub Wiki
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
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 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 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
{
// ...
}
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.
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;
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
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)