Project

General

Profile

Coding Styleguide » History » Version 17

quintus, 04/17/2021 05:57 PM
Add UTF-8 encoding requirement

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 9 quintus
  --attach-classes --attach-namespaces --attach-closing-while --attach-inlines --attach-extern-c \
12 8 quintus
  --align-pointer=type --align-reference=type \
13 16 quintus
  --break-one-line-headers --add-braces --close-templates --lineend=linux \
14 8 quintus
   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 17 quintus
## File encoding
22
23
All source code files have to be encoded in UTF-8.
24
25 8 quintus
## Commentary
26 1 quintus
27 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.
28 1 quintus
29
~~~~~ c++
30 8 quintus
// Careful: this is used in foobar() as well
31
32
/* This code is designed to specifically fit the purpose of an example.
33
 * It was not at all easy to come up with all this text, but for the
34
 * sake of an example, it was required to do so. */
35
~~~~~
36
37 1 quintus
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.
38 16 quintus
39
## Line endings
40
41
All files use only LF as line endings (unix-style line endings).
42 8 quintus
43
## Indentation and line length
44
45
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.
46
47
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c++
48
#ifndef MY_HEADER
49
#define MY_HEADER
50
51
namespace MyNamespace {
52
    class MyClass {
53
    public:
54
        MyClass();
55
       ~MyClass();
56
    };
57
58 11 quintus
    struct MysSruct {
59 8 quintus
        int a;
60
        int b;
61
    };
62
}
63
64 11 quintus
void myFunc()
65 8 quintus
{
66 11 quintus
    foo();
67 8 quintus
    if (something) {
68 11 quintus
        bar();
69
        baz();
70 8 quintus
    }
71
}
72
#endif
73
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74
75
Lines should be broken around 80 characters, and the resulting continuation lines should be indented so that it makes sense to look at. Example:
76
77
~~~~~ c++
78 1 quintus
if (somecondition) {
79 11 quintus
    thisIsAVeryLongFunctionName(this_is_a_parameter,
80 1 quintus
                                this_is_another_parameter,
81
                                third_parameter);
82
}
83
~~~~~
84
85 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.
86 1 quintus
87 8 quintus
## Placement of braces
88 1 quintus
89 9 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 ordinary 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).
90 1 quintus
91 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.
92 1 quintus
93 8 quintus
~~~~~~~~~~~ c++
94
// Exception: function definition
95
void main()
96
{
97
    // ...
98 1 quintus
}
99
100 9 quintus
// Member functions also fall under the exception.
101 11 quintus
void Foo::setX(int x)
102 9 quintus
{
103
    m_x = x;
104
}
105
106 8 quintus
// Everything else: attach the braces.
107
class Foo {
108
    // ...
109
};
110 10 quintus
struct Foo {
111 8 quintus
    // ...
112
};
113 10 quintus
enum class Foo {
114 8 quintus
    // ...
115
};
116
if (condition) {
117
    // ...
118
}
119
while (condition) {
120
    // ...
121 1 quintus
}
122
123 9 quintus
// A function definition inside a class definition does not count as an "ordinary" function. Attach.
124
// This is mostly useful for defining short class constructors or getters/setters.
125
class Foo {
126 12 quintus
    Foo(int x) : m_x {}
127
    inline int getX() { return m_x; }
128 9 quintus
};
129
130 8 quintus
// Trailing while attached
131
{
132
    // ...
133
} while (condition)
134
135
// Brace kuddling from 1TBS
136
if (condition1) {
137
    // ...
138
} else if (condition2) {
139
    // ...
140
} else {
141
    // ...
142
}
143
144
// Required braces around one-line statements
145
if (condition) {
146
    doit();
147
}
148
149
// Also requires braces
150
if (condition1) {
151
    doit();
152
} else { // Thanks to brace cuddling, this is not as bad as in pure K&R
153
    doother();
154
}
155
156
// DO NOT DO THIS
157 11 quintus
if (condition) { oneliner(); }
158 8 quintus
// Break it.
159
if (condition) {
160 11 quintus
    oneliner();
161 8 quintus
}
162
~~~~~~~~~~~
163
164
## Initial assignment
165
166
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?".
167
168
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c++
169
int a;     // BAD
170
int a = 0; // GOOD
171
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
172
173
## Pointer and reference alignment, multi-variable declarations
174
175
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:
176
177
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c++
178
int i = 0, j = 0; // Okay, no pointer
179
180
int* p  = nullptr;         // Pointer to int
181
int* p1 = nullptr, p2 = 0; // DONT DO THIS. p2 is not a pointer!
182
183
// Instead, break the lines up.
184
int* p1 = nullptr;
185
int  p2 = 0;
186
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
187
188
Multiple variable assignments should be aligned at the `=`.
189
190
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c++
191
int foo    = 0;
192
int foobar = 0;
193
int la     = 0;
194
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
195
196
## Parantheses and spacing
197
198
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*.
199
200
Template angles and index brackets do not have any surrounding space.
201
202
~~~~~~~~ c++
203 11 quintus
void foo(int myparam) // No spaces around the "(" and ")"
204 8 quintus
{
205 1 quintus
    if (condition) { // One space between keyword if and "(", and one space between ")" and "{"
206 11 quintus
        myFunc(5);   // No spaces around the "(" and ")"
207 1 quintus
    }
208
}
209 8 quintus
210
vector[3] = foo;                // No space between "vector" and "[3]"
211
map<string, vector<int>> mymap; // No spaces around <> (C++11 syntax)
212
~~~~~~~~
213
214 1 quintus
## Case of identifiers
215 8 quintus
216 1 quintus
* Macros are ALL_IN_CAPS. They need to stand out, because macro expansion can have surprises which may explain cryptic error messages.
217 11 quintus
* Variable identifiers use snake_case, including identifiers for variables declared `const` and/or `static`. The different variable types are distinguished by the prefix (see [below](#Abbreviated-Hungarian-Notation)).
218
* Function identifiers use snakedCamelCase (i.e. first word is lowercase, the rest is CamelCase). Constructors and destructors are the only exception to this rule (because C++’ syntax requires them to be the same way as the type identifier).
219
* Type identifiers (class, enum, struct identifiers) use CamelCase.
220 10 quintus
221 11 quintus
The different casing rules make it easy to spot which kind of identifier one is dealing with. Between one-word functions and variables a distinction is not possible except for the variable prefix; it is expected that most function identifiers are composed of multiple words, so that this is not an issue.
222
223 1 quintus
~~~~~~ c++
224
#define THIS_IS_A_MACRO(x) foo_ ## x
225
226 10 quintus
struct MyStruct;
227
enum class MyEnum;
228 1 quintus
229
class MyClass {
230
    MyClass();
231
    ~MyClass();
232
233 11 quintus
    void memberFunction();
234
    static int staticMemberFunction();
235 1 quintus
236
    int m_member_variable;
237
    static int static_member;
238 3 quintus
};
239 1 quintus
240 11 quintus
void foo()
241 8 quintus
{
242 3 quintus
    static int local_static_variable;
243 1 quintus
    float normal_local_var = 3.5f;
244 3 quintus
    // ...
245 1 quintus
}
246 3 quintus
~~~~~~
247
248 1 quintus
## Abbreviated Hungarian Notation
249 3 quintus
250
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.
251 1 quintus
252
| Prefix | Meaning                                                          |
253
|--------+------------------------------------------------------------------|
254
|        | No prefix: Local variable                                        |
255
| m      | Member variable                                                  |
256
| f      | File-local variable                                              |
257
| g      | Global variable                                                  |
258
| p      | Variable holds a pointer (both raw and managed pointers)         |
259
| a      | Variable holds a raw array (not: vector or other C++ containers) |
260
261
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.
262
263
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.
264
265
~~~~~~~~~~~ c++
266
static int f_something; // File-local variable
267
extern int g_globvar;   // Global variable
268
269 8 quintus
class MyClass {
270 11 quintus
    void myFunc() {
271 1 quintus
        int* p_int;                   // Local variable
272
        m_normal_member += "AAA";     // Accessing member variable
273 11 quintus
        doSomething(MyClass::foobar); // Exception: accessing static member variable via class name, not directly
274 1 quintus
    }
275 7 quintus
276 1 quintus
    std::string m_normal_member;    // Normal member variable
277
    int* mp_int;                    // Member variable with pointer
278
    static const float foobar = 42; // Exception: Static member variable
279
};
280
281 10 quintus
struct Point {
282 1 quintus
    int x;            // Struct members have no "m" prefix
283 7 quintus
    int y;
284
    int z;
285
    owner* p_owner;   // But they do have the type prefix if required.
286
};
287 8 quintus
288 13 quintus
Point moveUp(Point p)
289 8 quintus
{
290 1 quintus
    p.y -= 10;         // Access to struct member without "m"
291
    return p;
292 7 quintus
}
293
294 10 quintus
enum class Color { red, green, blue }; // Exception: enum members do not have "m"
295 11 quintus
void myFunc(color c)
296 8 quintus
{
297 10 quintus
    if (c == Color::red) { // Because they are accessed only from the outside.
298 7 quintus
      // ...
299
    }
300
}
301
~~~~~~~~~~~
302
303
## enum specifics
304
305
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).
306
307
~~~~~~ c++
308 10 quintus
enum class Color { red, green, blue };
309 7 quintus
310 11 quintus
void foo(Color c)
311 8 quintus
{
312 7 quintus
    // ...
313
}
314
~~~~~~
315
316 14 quintus
## Use of namespaces
317
318
If the STL is used, always include the STL namespace at the top of the `.cpp` file. The same goes for internal namespaces (that is, those that are defined by the game itself).
319
320
~~~~~ c++
321
#include <iostream>
322
323
using namespace std;
324
using namespace InternalNameSpace;
325
326
void main()
327
{
328
    cout << "Doing something" << endl; // Actually std::cout and std::endl
329
    doIt(); // Actually InternalNamespace::DoIt()
330
}
331
~~~~~
332
333
Library namespaces should normally be spelled out in full, unless they are exceptionally long. In that case, abbreviate them with `namespace` at the top of the `.cpp` file.
334
335
~~~~~~~ c++
336
namespace LLN = LongLibraryNamespace;
337
// ...
338
LLN::foo var;
339
~~~~~~~
340
341
If a library namespace is needed really often in a function, you can include it _in that function only_.
342
343
~~~~~~~ c++
344
void Foo::someFunction()
345
{
346
    using namespace MyLib;
347
    // All the below functions come from the MyLib namespace
348
    doA();
349
    doB();
350
    doC(0);
351
    doD("foo");
352
}
353
~~~~~~~
354
355 7 quintus
## File names
356
357 1 quintus
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`.
358
359 14 quintus
## Guidelines for headers
360 1 quintus
361 14 quintus
### Header guards
362 1 quintus
363 14 quintus
All headers use header guards. The header guard is composed from the filename without the file extension coverted to CAPITAL_LETTERS.
364 1 quintus
365 14 quintus
~~~~~ c++
366
// my_file.hpp
367
#ifndef MY_FILE
368
#define MY_FILE
369
// ...
370
#endif
371
~~~~~
372 7 quintus
373 14 quintus
### Namespaces in headers
374
375
Namespaces in headers are always spelled out in full and not abbreviated or included. This includes the STL namespace if it is used. This way the inclusion of a header does not define unexpected new namespaces.
376
377
~~~~~~ c++
378
namespace MyNamespace {
379
    class MyClass {
380
        MyClass();
381
        std::vector<std::string> m_my_values;
382
    }
383
}
384 7 quintus
~~~~~~~
385
386 15 quintus
### Forward declarations in headers
387 7 quintus
388 8 quintus
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.
389 11 quintus
390 7 quintus
~~~~~~~~~~ c++
391
// Forward declarations
392
class MyClass;
393
394
class MyOtherClass {
395 1 quintus
public:
396
    MyOtherClass(MyClass& mc) : m_mc(mc){}
397
    void foo(MyClass* p_mc){ /* ... */ }
398
private:
399
    MyClass& m_mc;
400
};
401
~~~~~~~~~~
402
403
The corresponding `.cpp` file will then have to include the internal header for `MyClass`:
404
405
~~~~~~ c++
406
#include "my_other_class.hpp"
407
#include "../misc/my_class.hpp" // <---
408
~~~~~~
409 14 quintus
410
### Inclusion of headers
411
412
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:
413
414
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.
415
2. Other internal headers.
416
3. External library headers.
417
4. Standard library headers.
418
419
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 ""`.
420
421
~~~~~~~ c++
422
// This is foo.cpp, it has a header foo.hpp.
423
#include "foo.hpp"
424
#include "../misc/internal_header.hpp"
425
#include <curl.h>
426
#include <vector>
427
#include <cstdlib>
428
~~~~~~~