comparison STYLE_CPP.md @ 118:841e39c8aba4

Misc: add STYLE_CPP.md
author David Demelier <markand@malikania.fr>
date Fri, 22 Sep 2017 09:32:27 +0200
parents
children 835c8ee3f9e5
comparison
equal deleted inserted replaced
117:8a5d022aa6d5 118:841e39c8aba4
1 Malikania Engine C++ CODING STYLE
2 =================================
3
4 Style
5 -----
6
7 - Always use 4 spaces as indentation,
8 - Use UTF-8 charset,
9 - Use Unix line endings,
10 - Do not exceed 120 characters for lines of code,
11 - Do not exceed 80 characters for comments,
12 - Never write two blank consecutives blank lines,
13 - Do not use bad words.
14
15 ### Braces
16
17 Braces follow the K&R style, they are never placed on their own lines except for
18 function definitions.
19
20 Do not put braces for single line statements except for clarity.
21
22 if (condition) {
23 apply();
24 add();
25 } else
26 ok();
27
28 if (condition)
29 validate();
30
31 if (foo) {
32 state = long + conditional + that + requires + several + lines +
33 to + complete;
34 }
35
36 Functions require braces on their own lines.
37
38 void function()
39 {
40 }
41
42 And a lambda has its braces on the same lines too:
43
44 sort([&] (auto&) {
45 return true;
46 });
47
48 ### Spaces
49
50 Each reserved keyword (e.g. `if`, `for`, `while`) requires a single space before
51 its argument.
52
53 Normal function calls do not require it.
54
55 if (foo)
56 destroy(sizeof (int));
57
58 ### References and pointers
59
60 References and pointers are always next to the type name and not the variable.
61
62 T& get(const std::string& name);
63
64 int* p = &x;
65
66 ### Naming
67
68 - English names,
69 - Member variables have trailing underscore (e.g foo\_bar\_),
70 - No hungarian notation.
71
72 Everything is in `underscore_case` except template parameters and macros.
73
74 #if defined(FOO)
75 # include <foo.hpp>
76 #endif
77
78 namespace baz {
79
80 class object {
81 private:
82 std::string name_;
83
84 public:
85 inline const std::string& name() const noexcept
86 {
87 return name_;
88 }
89 };
90
91 template <typename Archive>
92 void open(const Archive& ar)
93 {
94 bool is_valid = false;
95 }
96
97 } // !baz
98
99 ### Header guards
100
101 Do not use `#pragma once`.
102
103 Header guards are usually named **PROJECT_COMPONENT_FILENAME_HPP**.
104
105 #ifndef FOO_COMMON_UTIL_HPP
106 #define FOO_COMMON_UTIL_HPP
107
108 #endif // !FOO_COMMON_UTIL_HPP
109
110 ### Enums
111
112 Enumerations constants are always defined in separate line to allow commenting
113 them as doxygen.
114
115 Enum class are encouraged.
116
117 enum class color {
118 blue,
119 red,
120 green
121 };
122
123 ### Files
124
125 - Use `.cpp` and `.hpp` as file extensions,
126 - Filenames are all lowercase.
127
128 ### Comments
129
130 Avoid useless comments in source files. Comment complex things or why it is done
131 like this. However any public function in the .hpp **must** be documented as
132 doxygen without exception.
133
134 /*
135 * Multi line comments look like
136 * this.
137 */
138
139 // Short comment
140
141 Use `#if 0` to comment blocks of code.
142
143 #if 0
144 broken_stuff();
145 #endif
146
147 ### Includes
148
149 The includes should always come in the following order.
150
151 1. C++ headers
152 2. C header
153 3. Third party libraries
154 4. Application headers in ""
155
156 #include <cstring>
157 #include <cerrno>
158
159 #include <sys/stat.h>
160
161 #include <libircclient.h>
162
163 #include "foo.h"
164
165 **Note**: always use C++ headers for C equivalent, stdio.h -> cstdio, etc.
166
167 ### Commit messages
168
169 Commit messages are written using the following syntax:
170
171 Topic: short message less than 80 characters
172
173 Optional additional description if needed.
174
175 Replace `Topic` with one of the following:
176
177 - **Client**: client library and executable,
178 - **CMake**: for the build system,
179 - **Common**: common library,
180 - **Docs**: for the documentation,
181 - **Misc**: for miscellaneous files,
182 - **Server**: server library and executable,
183 - **Tests**: for the unit tests.
184
185 Programming
186 -----------
187
188 ### C language
189
190 Do not use old C stuff like `void *`, `srand/rand`, `printf` or anything that
191 can be rewritten in modern C++.
192
193 ### RTTI
194
195 Usage of `dynamic_cast` and `typeid` are completely disallowed in any shape of
196 form.
197
198 ### Arguments
199
200 It is recommended to pass parameters by value or const reference. Usage of
201 non-const reference as output parameter is **discouraged** and should be avoided
202 in many case because it does not allow chaining of expressions like:
203
204 std::cout << reverse(upper(clean(" hello world! "))) << std::endl;
205
206 If your function is designed to return a modified value passed as argument, it
207 is better to take it by value and modify it directly.
208
209 std::string clean(std::string input)
210 {
211 if (!input.empty() && input.back() == '\r')
212 input.pop_back();
213
214 return input;
215 }
216
217 Never pass primitive types as const value.
218
219 ### Assertions
220
221 Use the `assert` macro from the cassert header file to verify programming
222 errors.
223
224 For example, you may use `assert` to verify that the developer access the data
225 between the bounds of an array:
226
227 T& operator[](unsigned index)
228 {
229 assert(index < length_);
230
231 return data_[index];
232 }
233
234 The `assert` macro is not meant to check that a function succeeded, this code
235 must not be written that way:
236
237 assert(listen(10));
238
239 ### Exceptions
240
241 You must use exceptions to indicate an error that was unexpected such as:
242
243 - Failing to open a file,
244 - I/O unexpected errors,
245 - Parsing errors,
246 - User errors.
247
248 You may use the C++ standard exceptions defined in the stdexcept header but if
249 you need to carry more data within your exception, you should derive from
250 `std::exception`.
251
252 ### Error code
253
254 You should not use error codes to indicate errors, instead use exceptions.
255 Error codes are allowed in Boost.Asio though.
256
257 ### Free functions
258
259 Basic utility functions should be defined in a namespace as a free function not
260 as a static member function, we're doing C++ not Java.
261
262 Example:
263
264 namespace util {
265
266 std::string clean(std::string input);
267
268 } // !util
269
270 ### Variables initialization
271
272 Use parentheses to initialize non primitive types:
273
274 throw std::runtime_error("foo");
275
276 my_class obj("bar");
277
278 Use brace initialization when you want to use an initializer list, type
279 elision:
280
281 std::vector<int> v{1, 2, 3};
282
283 foo({1, 2}); // type deduced
284
285 return { "true", false }; // std::pair returned
286
287 Use the assignment for primitive types:
288
289 int x = 123;
290 bool is_valid = true;
291
292 ### Classes
293
294 Classes are usually defined in the following order:
295
296 1. Public inner types (enums, classes),
297 2. Protected/private members
298 3. Public functions
299
300 class foo {
301 public:
302 enum class type {
303 a,
304 b
305 };
306
307 private:
308 int member_{0};
309
310 public:
311 void some_function();
312 };
313
314 ### Structs
315
316 Do not use C structs unless you have very good reason to do so. If you want to
317 pack some data, just use `class` and make all fields public.
318
319 class point {
320 public:
321 int x{0};
322 int y{0};
323 };
324
325 ### Return
326
327 The preferred style is to return early in case of errors. That makes the code
328 more linear and not highly indented.
329
330 This code is preferred:
331
332 if (a_condition_is_not_valid)
333 return nullptr;
334 if (an_other_condition)
335 return nullptr;
336
337 auto x = std::make_shared<object>();
338
339 x->start();
340 x->save();
341
342 return x;
343
344 Additional rules:
345
346 - Do never put parentheses between the returned value,
347 - Do not put a else branch after a return.
348
349 ### Auto
350
351 We encorage usage of `auto`, it reduces code maintainance as you don't need to
352 change your code when your rename types.
353
354 ````cpp
355 auto it = std::find_if(v.begin(), v.end(), [&] (const auto& obj) {
356 return obj.key() == "foo";
357 });
358
359 for (const auto& pair : a_map)
360 std::cout << pair.first << " = " << pair.second << std::endl;
361 ````
362
363 But do not use `auto` to write code like in python, this is not acceptable:
364
365 ````cpp
366 auto o = my_object("foo");
367 ````