Project

General

Profile

Coding Styleguide » History » Version 8

quintus, 03/24/2020 10:53 AM

1 1 quintus
# Coding Styleguide
2
3 2 quintus
{{toc}}
4
5 1 quintus
Those who have worked on the TSC project will remember the dreaded discussion about coding style. This project will avoid the trap by using a consistent coding style right from the start. It is outlined in this document.
6
7 8 quintus
The game's source code is formatted mostly according to the [1TBS](http://astyle.sourceforge.net/astyle.html#_style=1tbs) style, which is a variant of K&R, with slight adjustments. Details are documented below. Most (not all!) rules can be followed by formatting with the [astyle](http://astyle.sourceforge.net) command like this:
8 1 quintus
9 8 quintus
~~~~
10
$ astyle --style=1tbs --indent=spaces=4 --indent-namespaces \
11
  --attach-classes --attach-namespaces --attach-closing-while --attach-extern-c \
12
  --align-pointer=type --align-reference=type \
13
  --break-one-line-headers --add-braces --close-templates \
14
   yourfile.cpp
15
~~~~
16
17 1 quintus
In general, try to keep your code readable. Specifically, it is often useful to leave empty lines to separate logically grouped statements from one another.
18
19 8 quintus
One of the design goals of this document is to keep the styling consequent and easy to follow. For instance, instead of the questionable distinction of attaching braces to structs, but not to classes in the 1TBS style, this style says to simply always attach the braces (the only exception being function definitions). In so far it includes elements from the Stroustrup style. Likewise, indenting every brace block is easier to remember than not to indent namespaces.
20 1 quintus
21 8 quintus
## Commentary
22 1 quintus
23 8 quintus
Semantically, comments should normally reflect why code is written the way it is. Normally it is not required to explain what code does, unless it is an exceptionally complex part. Syntactically, use `//` for one and two lines of comments. Starting with the third line, comments should use the bock syntax `/* ... */`. In the block syntax, align each star with the one on the preceeding line. Terminate the comment block on the last line of the comment.
24 1 quintus
25
~~~~~ c++
26 8 quintus
// Careful: this is used in foobar() as well
27
28
/* This code is designed to specifically fit the purpose of an example.
29
 * It was not at all easy to come up with all this text, but for the
30
 * sake of an example, it was required to do so. */
31
~~~~~
32
33
Where Doxygen is used to generate documentation, use Doxygen's `///` markers for short and the `/**` markers for long documentation. Other than that, the above advice applies.
34
35
## Indentation and line length
36
37
Source code is indented with 4 spaces, tabs are not used. All blocks enclosed in braces are indented, including namespaces. Do not indent labels, including visibility labels like `public` and `private`; they should line up with the enclosing statement. Preprocessor statements are not indented.
38
39
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c++
40
#ifndef MY_HEADER
41
#define MY_HEADER
42
43
namespace MyNamespace {
44
    class MyClass {
45
    public:
46
        MyClass();
47
       ~MyClass();
48
    };
49
50
    struct mystruct {
51
        int a;
52
        int b;
53
    };
54
}
55
56
void MyFunc()
57
{
58
    foo();
59
    if (something) {
60
        bar();
61
        baz();
62
    }
63
}
64
#endif
65
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
66
67
Lines should be broken around 80 characters, and the resulting continuation lines should be indented so that it makes sense to look at. Example:
68
69
~~~~~ c++
70 1 quintus
if (somecondition) {
71
    thisIsAVeryLongFunctionName(this_is_a_parameter,
72
                                this_is_another_parameter,
73
                                third_parameter);
74
}
75
~~~~~
76
77 8 quintus
This is not a hard requirement. You should use whatever conveys the meaning best. That's why the `astyle` command above does not include a hard line wrap option.
78 1 quintus
79 8 quintus
## Placement of braces
80 1 quintus
81 8 quintus
Braces are placed on the same line like the statement they belong to, be that a class or namespace declaration, an `extern C`, or anything else. The only exception from this are function definitions: for them the opening brace is broken into the next line. In any case, the closing brace always has its own line (makes it easy to spot the end of a block). `if/elseif/else` is cuddled to keep code compact, and the rare case of a trailing `while` has the `while` attached to the brace. Do not leave out braces even for one-line statements. This should prevent any accidental cutting of conditional clauses. To keep style consistent, also do not use all-in-one-line conditionals (this violates the expectation that the closing brace for each block can be found on its own line at the relevant indentation level).
82 1 quintus
83 8 quintus
Keen readers will notice that for in-function statements this uses the 1TBS style, and for out-of-function statements the Stroustrup style. Examples below.
84 1 quintus
85 8 quintus
~~~~~~~~~~~ c++
86
// Exception: function definition
87
void main()
88
{
89
    // ...
90
}
91 1 quintus
92 8 quintus
// Everything else: attach the braces.
93
class Foo {
94
    // ...
95
};
96
struct foo {
97
    // ...
98
};
99
enum class foo {
100
    // ...
101
};
102
if (condition) {
103
    // ...
104
}
105
while (condition) {
106
    // ...
107
}
108 1 quintus
109 8 quintus
// Trailing while attached
110
{
111
    // ...
112
} while (condition)
113
114
// Brace kuddling from 1TBS
115
if (condition1) {
116
    // ...
117
} else if (condition2) {
118
    // ...
119
} else {
120
    // ...
121
}
122
123
// Required braces around one-line statements
124
if (condition) {
125
    doit();
126
}
127
128
// Also requires braces
129
if (condition1) {
130
    doit();
131
} else { // Thanks to brace cuddling, this is not as bad as in pure K&R
132
    doother();
133
}
134
135
// DO NOT DO THIS
136
if (condition) { oneliner(); }
137
// Break it.
138
if (condition) {
139
    oneliner();
140
}
141
~~~~~~~~~~~
142
143
## Initial assignment
144
145
Assign an initial value to any variable you declare. This prevents undefined values from coming from a forgotten assignment and eases debugging quite a bit, because it obviates the question "is this a real value or was this variable never assigned?".
146
147
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c++
148
int a;     // BAD
149
int a = 0; // GOOD
150
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
151
152
## Pointer and reference alignment, multi-variable declarations
153
154
Place the `*` and `&` next to the type, not to the name. They belong semantically to the type. Do not declare multiple variables in one line if one of them is a pointer, it causes confusion:
155
156
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c++
157
int i = 0, j = 0; // Okay, no pointer
158
159
int* p  = nullptr;         // Pointer to int
160
int* p1 = nullptr, p2 = 0; // DONT DO THIS. p2 is not a pointer!
161
162
// Instead, break the lines up.
163
int* p1 = nullptr;
164
int  p2 = 0;
165
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
166
167
Multiple variable assignments should be aligned at the `=`.
168
169
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c++
170
int foo    = 0;
171
int foobar = 0;
172
int la     = 0;
173
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
174
175
## Parantheses and spacing
176
177
Between a keyword and the opening paranthesis is exactly one space. Between the closing paranthesis and the opening curly brace is exactly one space as well. *There is no space between a function name and the opening paranthesis of its argument list*.
178
179
Template angles and index brackets do not have any surrounding space.
180
181
~~~~~~~~ c++
182
void Foo(int myparam) // No spaces around the "(" and ")"
183
{
184
    if (condition) { // One space between keyword if and "(", and one space between ")" and "{"
185
        MyFunc(5);   // No spaces around the "(" and ")"
186
    }
187
}
188
189
vector[3] = foo;                // No space between "vector" and "[3]"
190
map<string, vector<int>> mymap; // No spaces around <> (C++11 syntax)
191
~~~~~~~~
192
193 1 quintus
## Case of identifiers
194
195
* Macros are ALL_IN_CAPS. They need to stand out.
196
* Class identifiers use CamelCase. Structure and enum identifiers are snake_case (makes it easy to spot whether this type is copy-by-value or copy-by-reference: every lowercase type is copy-by-value).
197
* Member function identifiers are CamelCase. This keeps function names short (they tend to be longer than member variable names).
198
* All variables and constants (unless the constants are macros, see above) are snake_case. This includes static member variables, even if they are constant. This way a non-function member is easily identifiable by being lowercase. The different variable types are distinguished by the prefix (see [below](#Abbreviated-Hungarian-Notation)).
199
200
~~~~~~ c++
201
#define THIS_IS_A_MACRO(x) foo_ ## x
202
203
struct my_struct;
204
enum class my_enum;
205
206
class MyClass {
207
    MyClass();
208
    ~MyClass();
209
210
    void MemberFunction();
211
    static int StaticMemberFunction();
212
213
    int m_member_variable;
214
    static int static_member;
215 3 quintus
};
216 1 quintus
217 8 quintus
void foo()
218
{
219 3 quintus
    static int local_static_variable;
220 1 quintus
    float normal_local_var = 3.5f;
221 3 quintus
    // ...
222 1 quintus
}
223 3 quintus
~~~~~~
224
225 1 quintus
## Abbreviated Hungarian Notation
226 3 quintus
227 1 quintus
Identifiers of variables and constants begin with a short sequence of characters that encodes some important information about the variable in question. This is called [Hungarian Notation](https://en.wikipedia.org/wiki/Hungarian_notation), but in full, it is cumbersome to read and leads to long identifier names. The following prefix characters have been chosen with respect to two goals: Make variable scope immediately visible, and warn of "unusual" types.
228 3 quintus
229 1 quintus
| Prefix | Meaning                                                          |
230 3 quintus
|--------+------------------------------------------------------------------|
231 1 quintus
|        | No prefix: Local variable                                        |
232
| m      | Member variable                                                  |
233
| f      | File-local variable                                              |
234
| g      | Global variable                                                  |
235
| p      | Variable holds a pointer (both raw and managed pointers)         |
236
| a      | Variable holds a raw array (not: vector or other C++ containers) |
237
238
The scope prefix comes before the type prefix. Thus, `mp_foo` is a member variable holding a pointer, and `ga_argv` is a global variable holding a raw C array.
239
240
There are two special cases. First, member variables of structs and enums do not have a leading `m` prefix, because they do not normally contain functions, but are only accessed from the outside (whereas for classes as per the secrecy principle access to member variables from the outside is unusual), and it would be cumbersome to always add the extra `m`. Second, static member variables of classes do not have a scope prefix. Instead, they are always to be accessed via the class name.
241
242
~~~~~~~~~~~ c++
243
static int f_something; // File-local variable
244
extern int g_globvar;   // Global variable
245
246
class MyClass {
247 8 quintus
    void MyFunc()
248
    {
249 1 quintus
        int* p_int;                   // Local variable
250
        m_normal_member += "AAA";     // Accessing member variable
251
        DoSomething(MyClass::foobar); // Exception: accessing static member variable via class name, not directly
252
    }
253 7 quintus
254 1 quintus
    std::string m_normal_member;    // Normal member variable
255
    int* mp_int;                    // Member variable with pointer
256
    static const float foobar = 42; // Exception: Static member variable
257
};
258
259
struct point {
260
    int x;            // Struct members have no "m" prefix
261 7 quintus
    int y;
262
    int z;
263
    owner* p_owner;   // But they do have the type prefix if required.
264
};
265
266 8 quintus
point MoveUp(point p)
267
{
268 7 quintus
    p.y -= 10;         // Access to struct member without "m"
269
    return p;
270
}
271
272
enum class color { red, green, blue }; // Exception: enum members do not have "m"
273 8 quintus
void MyFunc(color c)
274
{
275 7 quintus
    if (c == color::red) { // Because they are accessed only from the outside.
276
      // ...
277
    }
278
}
279
~~~~~~~~~~~
280
281
## enum specifics
282
283
Names of `enum` identifiers are singular, not plural. If used as a type, `color var` reads more natural than `colors var`. Use `enum class` instead of raw `enum` whenever possible (this is C++11 specific and allows colliding enum identifiers in case you wonder that `enum class` is valid syntax).
284
285
~~~~~~ c++
286
enum class color { red, green, blue };
287
288 8 quintus
void Foo(color c)
289
{
290 7 quintus
    // ...
291
}
292
~~~~~~
293
294
## File names
295
296
All source code files are in snake_case. C++ source code files end in `.cpp`, C++ headers end in `.hpp`. C source files end with `.c`, C headers in `.h`.
297
298
## Inclusion of headers
299
300
Each C/C++ source file includes only the headers it needs to compile, and all inclusions are at the top of the file. Inclusions are done in this order:
301
302
1. The header file corresponding to this `.c`/`.cpp` file. Doing this first ensures that the `.hpp` file is self-contained, because on compilation of the corresponding `.cpp` file the compiler will error out on the very first `#include` line then if the header is not self-contained. For `.hpp` files, this step is obviously missing.
303
2. Other internal headers.
304
3. External library headers.
305
4. Standard library headers.
306
307
The path delimiter for `#include` statements is always a forward slash, because this compiles on both Windows and Unix systems. External and standard library headers are included with angle `#include <>`, internal headers with quoted `#include ""`.
308
309
~~~~~~~ c++
310
// This is foo.cpp, it has a header foo.hpp.
311
#include "foo.hpp"
312
#include "../misc/internal_header.hpp"
313
#include <curl.h>
314
#include <vector>
315
#include <cstdlib>
316
~~~~~~~
317
318
## Forward declarations in headers
319
320
To increase compile times and keep the inclusion graph simple, headers should try hard to not require other internal headers. If an internal type is required in a header, it should be forward-declared in that very header. Note that it is possible to forward-declare classes, structs, and even enums. In most cases, forward-declarations are entirely sufficient. For instance, pointers, references, many templated types, and even smart pointers can be used with only the forward-declaration available.
321
322
~~~~~~~~~~ c++
323
// Forward declarations
324
class MyClass;
325
326
class MyOtherClass {
327
public:
328 8 quintus
    MyOtherClass(MyClass& mc) : m_mc(mc){}
329 7 quintus
    void Foo(MyClass* p_mc){ /* ... */ }
330
private:
331
    MyClass& m_mc;
332
};
333
~~~~~~~~~~
334
335
The corresponding `.cpp` file will then have to include the internal header for `MyClass`:
336 1 quintus
337
~~~~~~ c++
338
#include "my_other_class.hpp"
339
#include "../misc/my_class.hpp" // <---
340
~~~~~~