C++ Code Style Guide
All of the C++ code in the project is styled, you should run any changes you make through astyle before pushing a pull request.
We are using astyle version 3.0.1. Version 3.1 should also work, though there are a few cases where they disagree and require annotation.
Blocks of code can be passed through astyle to ensure that their formatting is correct:
astyle --style=1tbs --attach-inlines --indent=spaces=4 --align-pointer=name --max-code-length=100 --break-after-logical --indent-classes --indent-preprocessor --indent-switches --indent-col1-comments --min-conditional-indent=0 --pad-oper --unpad-paren --pad-paren-in --add-brackets --convert-tabs
These options are mirrored in .astylerc
, doc/CODE_STYLE.txt
and
msvc-full-features/AStyleExtension-Cataclysm-BN.cfg
For example, from vi
, set marks a and b around the block, then:
:'a,'b ! astyle --style=1tbs --attach-inlines --indent=spaces=4 --align-pointer=name --max-code-length=100 --break-after-logical --indent-classes --indent-preprocessor --indent-switches --indent-col1-comments --min-conditional-indent=0 --pad-oper --unpad-paren --pad-paren-in --add-brackets --convert-tabs
See DEVELOPER_TOOLING.md for other environments.
Code Example
Here's an example that illustrates the most common points of style:
int foo( int arg1, int *arg2 )
{
if( arg1 < 5 ) {
switch( *arg2 ) {
case 0:
return arg1 + 5;
break;
case 1:
return arg1 + 7;
break;
default:
return 0;
break;
}
} else if( arg1 > 17 ) {
int i = 0;
while( i < arg1 ) {
printf( _( "Really long message that's pointless except for the number %d and for its "
"length as it's illustrative of how to break strings properly.\n" ), i );
}
}
return 0;
}
Code Guidelines
These are less generic guidelines and more pain points we've stumbled across over time.
-
Prefer immutable values and declare variables with
const
. Less moving parts mean more predictable code flow. -
Prefer
int
.long
in particular is problematic since it is not a larger type than int on some platforms we support.- Using integral value larger than 32 bits should be avoided. Use
int64_t
if it's really necessary. uint
is also a problem, it has poor behavior when overflowing and should be avoided for general purpose programming.- If you need binary data,
unsigned int
orunsigned char
are fine, but you should probably use astd::bitset
instead.
- If you need binary data,
float
is to be avoided, but has valid uses.
-
Use
auto
keyword where it makes sense to do so, for example:- Prefer trailing return types in function declarations. Long return types obscure function name and makes reading class methods a painful experience.
class Bar; auto foo( int a ) -> int { const Bar &bar = some_function(); return is_bar_ok( bar ) ? 42 : 404; }
- Use for
decltype
style generic functions
template<typename A, typename B> - decltype(std::declval<A&>() * std::declval<B&>()) multiply(A a, B b) + auto multiply( A a, B b ) -> decltype( a * b ) { return a*b; }
- Aliasing for long iterator declarations
std::map<int, std::map<std::string, some_long_typename>> some_map; - std::map<int, std::map<std::string, some_long_typename>>::iterator iter = some_map.begin(); + auto iter = some_map.begin();
- Required for Lambda declarations
auto two_times = []( int a ) { return a * 2; };
- Doesn't otherwise sacrifice readability for expedience. Options for inlay type hinting are available in popular code editor such as vscode.
-
Avoid
using namespace
for standard namespaces. -
Avoid adding new member methods to classes unless required.
// this function does not access non-public data members or member methods in the class, and thus can be made a free function - std::string Character::profession_description() const - { - return prof->description( male ); - } + auto profession_description( const Character &c ) -> std::string + { + return c.prof->description( c.male ); + }