Fabrique style guide
This is a style guide for Fabrique contributors. Following it should help us keep the quality of code high. In order of importance, we value:
- correctness: use constructs that prevent or expose logical errors
- readability: code will be read by humans as often as compilers
- consistency: code that deviates from the style should look “funny”
- aesthetics: it would be nice if the code wasn’t ugly
Critical
Everything in this list exposes insidious and difficult-to-debug errors. Changes to these requirements should only be made for a very, very good reason (not “it conflicts with my personal aesthetic sense”).
-
Avoid raw pointers.
- C++11 provides
unique_ptr
,shared_ptr
andweak_ptr
: use them! - Only use raw pointers when absolutely necessary (e.g. interfacing with lex/yacc).
- C++11 provides
-
Compile with
-Weverything
.-
Aside from the whitelisted warnings below, Fabrique should always build with all warnings. With Clang,
-Wall
gives a lot of warnings but-Weverything
actually produces all the warnings. -
Some of these warnings seem silly (who cares if a constructor takes a parameter with the same name as a member variable!?), but warnings are there for a reason. Fabrique should have very clean code.
-
However, please do not enable
-Werror
in the repository: it is very annoying for WIP code, so please rely on contributors’ self-discipline instead. -
We do whitelist a few warnings:
-
-Wno-c++89-compat[-pedantic]
- We make agressive use of C++11 and don’t care at all about C++98 compatibility.
-
-Wpadded
- We aren’t attempting to preserve an ABI. At least not yet
-
-
-
Use
const
liberally.- Immutability makes parallelism and bug-spotting easier.
- If something can be const, it probably should be.
-
Use
override
for virtual method overrides.- When overriding a method, declare what you’re doing explicitly.
- The compiler will tell you if you’re mistaken (e.g. the superclass declaration changes).
Important
Items in this category can prevent or expose bugs, but are less important and more open to argument than Critical guidelines.
-
Avoid C-style casts.
- C++-style casts express your intent more clearly.
- Use
static_cast
anddynamic_cast
as needed. - Use
const_cast
with caution. - Avoid
reinterpret_cast
.
-
Use whitespace to suggest logical relationships.
-
Unlike some style guides we could mention (ahem), Fabrique does not prioritise fitting inside 80x25 terminal windows over readability.
-
Put a blank line between tightly-coupled lines and less-coupled ones:
while (...) { unique_ptr<Foo> foo(GetMeAFoo()); unique_ptr<Bar> bar(foo->CreateBar()); foo.DoStuff(); bar.DoStuff(); foo.CompareStuff(bar); if (not foo.allDone()) foo.doStuff(); }
-
Align related/grouped operations:
socket.host.name = DefaultHostName(); socket.host.port = 12345; socket.flags = NeverReconnect;
-
-
Prefer keyword operators (
and
,or
) over symbolic ones (&&
,||
).- Simple typos can cause confusion between logical and bitwise operations.
- Prefer operators with a large Hamming distance between them.
-
Distinguish parentheses related to functions from other uses.
foo(x)
is a function call.while (x)
is not.
Preferences
In open-source style wars, some points really aren’t that important (e.g. tabs vs spaces). It is important to be consistent, however, so please abide by the following choices that have already been encoded in our codebase. Some of these choices will seem familiar if you know the Qt style guide, but others are fairly arbitrary. Any of these choices could be reversed if there’s a good reason to, but the more code is disrupted, the bigger the reason would need to be.
- Name simple accessors simply.
Foo::parent()
, notFoo::getParent()
- Do use accessors: they provide freedom of implementation down the road.
- Use
auto
to eliminate redundant type declarations but not to obfuscate variable types:
// Redundant type declaration: should use auto.
SomeType& x = dynamic_cast<SomeType>(y);
// Not enough type information.
auto& z = y.NoHintAsToWhatKindOfThingIsReturned();
// Stupidly-long declaration: should use auto.
map<string,vector<pair<int,bool>>>::iterator i = fileDescriptors.find(fileName);
-
Use
CamelCaseNames
.- Type names and constants (including enum values) start with a capital letter.
- Variable names start with a lower-case letter.
- Method names can go either way:
- Lower-case: accessors and other simple methods (e.g.
Foo::done()
). - Upper-case: methods that do “real work” (e.g.
Foo::CreateChild()
).
- Lower-case: accessors and other simple methods (e.g.
-
Give member variables an underscore suffix.
- The most sensible name for a member variable is probably also the best name for its accessors, so give the variable an underscore (and this will also help with
-Wshadow
).
- The most sensible name for a member variable is probably also the best name for its accessors, so give the variable an underscore (and this will also help with
-
Use tabs and spaces.
- Use one tab per indent level.
- Use spaces for within-indent-level alignment.