118
|
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 ```` |