Mercurial > malikania
comparison STYLE.md @ 182:3107ce017c3a
Misc: switch back to SDL
Qt Quick and QML was an exciting experiment but it's definitely not enough
flexible and easy to use for game development.
Using SDL2 will let us focusing on our own drawing functions without any kind of
overhead.
While here, start massive cleanup.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 19 Oct 2018 20:18:19 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
181:fbfc2555bda5 | 182:3107ce017c3a |
---|---|
1 PROJECT NAME CODING STYLE | |
2 ========================= | |
3 | |
4 File content | |
5 ============ | |
6 | |
7 - Use UTF-8 charset, | |
8 - Use Unix line endings, | |
9 - Never write two blank consecutives blank lines. | |
10 | |
11 Indent | |
12 ====== | |
13 | |
14 Use tabs to indent and spaces for alignment. Tabs are meant and designed for | |
15 indenting code and have the advantage of being configurable. On the other hand | |
16 to keep code clean, you must align content with spaces only *within* a line. | |
17 | |
18 Note: we recommend 8 columns to avoid high number of indentations. | |
19 | |
20 Example (show whitespace in your editor) | |
21 | |
22 ```cpp | |
23 class foo { | |
24 public: | |
25 enum type { | |
26 dummy_value, // dummy comment | |
27 other_value // other comment | |
28 }; | |
29 | |
30 void long_function_name(very_long_type x1, | |
31 very_long_type x2) | |
32 { | |
33 const map<string, string> m{ | |
34 { "hostname", "127.0.0.1" }, | |
35 { "port", "6667" } | |
36 }; | |
37 } | |
38 }; | |
39 ``` | |
40 | |
41 As a rule of thumb, tabs must always be all length. | |
42 | |
43 Example of incorrect usage: | |
44 | |
45 ```cpp | |
46 { "hostname", "127.0.0.1" }, | |
47 { "port", "6667" } | |
48 ``` | |
49 | |
50 This example will not align correctly if tabstops are not set to 8. | |
51 | |
52 C++ | |
53 === | |
54 | |
55 Style | |
56 ----- | |
57 | |
58 - Do not exceed 120 columns for lines of code, | |
59 - Do not exceed 80 columns for comments, | |
60 | |
61 ### Braces | |
62 | |
63 Braces follow the K&R style, they are never placed on their own lines except for | |
64 function definitions. | |
65 | |
66 Do not put braces for single line statements. | |
67 | |
68 ```cpp | |
69 if (condition) { | |
70 apply(); | |
71 add(); | |
72 } else | |
73 ok(); | |
74 | |
75 if (condition) | |
76 validate(); | |
77 | |
78 if (foo) | |
79 state = long + conditional + that + requires + several + lines + | |
80 to + complete; | |
81 ``` | |
82 | |
83 Functions require braces on their own lines. | |
84 | |
85 ```cpp | |
86 void function() | |
87 { | |
88 } | |
89 ``` | |
90 | |
91 And a lambda has its braces on the same lines too: | |
92 | |
93 ```cpp | |
94 sort([&] (auto&) { | |
95 return true; | |
96 }); | |
97 ``` | |
98 | |
99 ### Spaces | |
100 | |
101 Each reserved keyword (e.g. `if`, `for`, `while`) requires a single space before | |
102 its argument. | |
103 | |
104 Normal function calls do not require it. | |
105 | |
106 ```cpp | |
107 if (foo) | |
108 destroy(sizeof (int)); | |
109 ``` | |
110 | |
111 ### References and pointers | |
112 | |
113 References and pointers are always next to the type name and not the variable. | |
114 | |
115 ```cpp | |
116 auto get(const std::string& name) -> T&; | |
117 | |
118 int* p = &x; | |
119 ``` | |
120 | |
121 ### Trailing return syntax | |
122 | |
123 We use trailing return syntax everywhere, it has the following benefits: | |
124 | |
125 - Inner types don't need to be prefixed by class name, | |
126 - Functions are kept aligned correctly, focusing on the function name. | |
127 | |
128 ```cpp | |
129 auto func() -> std::string; | |
130 ``` | |
131 | |
132 ### Naming | |
133 | |
134 - English names, | |
135 - Member variables have trailing underscore (e.g foo\_bar\_), | |
136 - No hungarian notation. | |
137 | |
138 Everything is in `underscore_case` except template parameters and macros. | |
139 | |
140 ```cpp | |
141 #if defined(FOO) | |
142 # include <foo.hpp> | |
143 #endif | |
144 | |
145 namespace baz { | |
146 | |
147 class object { | |
148 private: | |
149 std::string name_; | |
150 | |
151 public: | |
152 auto name() const noexcept -> const std::string&; | |
153 }; | |
154 | |
155 template <typename Archive> | |
156 void open(const Archive& ar) | |
157 { | |
158 bool is_valid = false; | |
159 } | |
160 | |
161 } // !baz | |
162 ``` | |
163 | |
164 ### Header guards | |
165 | |
166 Do not use `#pragma once`. | |
167 | |
168 Header guards are usually named `PROJECT_COMPONENT_FILENAME_HPP`. | |
169 | |
170 ```cpp | |
171 #ifndef FOO_COMMON_UTIL_HPP | |
172 #define FOO_COMMON_UTIL_HPP | |
173 | |
174 #endif // !FOO_COMMON_UTIL_HPP | |
175 ``` | |
176 | |
177 ### Enums | |
178 | |
179 Enumerations constants are always defined in separate line to allow commenting | |
180 them as doxygen. | |
181 | |
182 Enum class are encouraged. | |
183 | |
184 ```cpp | |
185 enum class color { | |
186 blue, | |
187 red, | |
188 green | |
189 }; | |
190 ``` | |
191 | |
192 ### Switch | |
193 | |
194 In a switch case statement, you **must** not declare variables and not indent | |
195 cases. | |
196 | |
197 ```cpp | |
198 switch (variable) { | |
199 case foo: | |
200 do_some_stuff(); | |
201 break; | |
202 default: | |
203 break; | |
204 } | |
205 ``` | |
206 | |
207 ### Files | |
208 | |
209 - Use `.cpp` and `.hpp` as file extensions, | |
210 - Filenames are all lowercase. | |
211 | |
212 ### Comments | |
213 | |
214 Avoid useless comments in source files. Comment complex things or why it is done | |
215 like this. However any public function in the .hpp **must** be documented as | |
216 doxygen without exception. | |
217 | |
218 ```cpp | |
219 /* | |
220 * Multi line comments look like | |
221 * this. | |
222 */ | |
223 | |
224 // Short comment | |
225 ``` | |
226 | |
227 Use `#if 0` to comment blocks of code. | |
228 | |
229 ```cpp | |
230 #if 0 | |
231 broken_stuff(); | |
232 #endif | |
233 ``` | |
234 | |
235 ### Includes | |
236 | |
237 The includes should always come in the following order. | |
238 | |
239 1. C++ headers | |
240 2. C header | |
241 3. Third party libraries | |
242 4. Application headers in "" | |
243 | |
244 ```cpp | |
245 #include <cstring> | |
246 #include <cerrno> | |
247 | |
248 #include <sys/stat.h> | |
249 | |
250 #include <libircclient.h> | |
251 | |
252 #include "foo.h" | |
253 ``` | |
254 | |
255 Note: always use C++ headers for C equivalent, stdio.h -> cstdio, etc. | |
256 | |
257 Programming | |
258 ----------- | |
259 | |
260 ### C language | |
261 | |
262 Do not use old C stuff like `void*`, `srand/rand`, `printf` or anything that | |
263 can be rewritten in modern C++. | |
264 | |
265 ### RTTI | |
266 | |
267 Usage of `dynamic_cast` and `typeid` are completely disallowed in any shape of | |
268 form. | |
269 | |
270 ### Arguments | |
271 | |
272 It is recommended to pass parameters by value or const reference. Usage of | |
273 non-const reference as output parameter is **discouraged** and should be avoided | |
274 in many case because it does not allow chaining of expressions like: | |
275 | |
276 ```cpp | |
277 std::cout << reverse(upper(clean(" hello world! "))) << std::endl; | |
278 ``` | |
279 | |
280 If your function is designed to return a modified value passed as argument, it | |
281 is better to take it by value and modify it directly. | |
282 | |
283 ```cpp | |
284 auto clean(std::string input) -> std::string | |
285 { | |
286 if (!input.empty() && input.back() == '\r') | |
287 input.pop_back(); | |
288 | |
289 return input; | |
290 } | |
291 ``` | |
292 | |
293 Never pass primitive types as const value. | |
294 | |
295 ### Assertions | |
296 | |
297 Use the `assert` macro from the cassert header file to verify programming | |
298 errors. | |
299 | |
300 For example, you may use `assert` to verify that the developer access the data | |
301 between the bounds of an array: | |
302 | |
303 ```cpp | |
304 auto operator[](unsigned index) -> T& | |
305 { | |
306 assert(index < length_); | |
307 | |
308 return data_[index]; | |
309 } | |
310 ``` | |
311 | |
312 The `assert` macro is not meant to check that a function succeeded, this code | |
313 must not be written that way: | |
314 | |
315 ```cpp | |
316 assert(listen(10)); | |
317 ``` | |
318 | |
319 ### Exceptions | |
320 | |
321 You must use exceptions to indicate an error that was unexpected such as: | |
322 | |
323 - Failing to open a file, | |
324 - I/O unexpected errors, | |
325 - Parsing errors, | |
326 - User errors. | |
327 | |
328 You may use the C++ standard exceptions defined in the stdexcept header but if | |
329 you need to carry more data within your exception, you should derive from | |
330 `std::exception`. | |
331 | |
332 ### Error code | |
333 | |
334 You should not use error codes to indicate errors, instead use exceptions. | |
335 Error codes are allowed in Boost.Asio though. | |
336 | |
337 ### Free functions | |
338 | |
339 Basic utility functions should be defined in a namespace as a free function not | |
340 as a static member function, we're doing C++ not Java. | |
341 | |
342 Example: | |
343 | |
344 ```cpp | |
345 namespace util { | |
346 | |
347 auto clean(std::string input) -> std::string; | |
348 | |
349 } // !util | |
350 ``` | |
351 | |
352 ### Variables initialization | |
353 | |
354 Use parentheses to initialize non primitive types: | |
355 | |
356 ```cpp | |
357 throw std::runtime_error("foo"); | |
358 | |
359 my_class obj("bar"); | |
360 ``` | |
361 | |
362 Use brace initialization when you want to use an initializer list, type | |
363 elision: | |
364 | |
365 ```cpp | |
366 std::vector<int> v{1, 2, 3}; | |
367 | |
368 foo({1, 2}); // type deduced | |
369 | |
370 return { "true", false }; // std::pair returned | |
371 ``` | |
372 | |
373 Use the assignment for primitive types: | |
374 | |
375 ```cpp | |
376 int x = 123; | |
377 bool is_valid = true; | |
378 ``` | |
379 | |
380 ### Classes | |
381 | |
382 Classes are usually defined in the following order: | |
383 | |
384 1. Public inner types (enums, classes), | |
385 2. Protected/private members and functions | |
386 3. Public static functions. | |
387 3. Public member functions | |
388 4. Public virtual functions. | |
389 | |
390 ```cpp | |
391 class foo { | |
392 public: | |
393 enum class type { | |
394 a, | |
395 b | |
396 }; | |
397 | |
398 private: | |
399 int member_{0}; | |
400 | |
401 public: | |
402 void some_function(); | |
403 }; | |
404 ``` | |
405 | |
406 ### Structs | |
407 | |
408 Use structs for objects that only need to store public data and have no | |
409 invariants. For example PODs and traits match this criteria: | |
410 | |
411 ```cpp | |
412 struct point { | |
413 int x{0}; | |
414 int y{0}; | |
415 }; | |
416 | |
417 template <> | |
418 struct info_traits<point> { | |
419 template <typename Archive> | |
420 static void serialize(Archive& ar, const point& point) | |
421 { | |
422 ar.write(point.x); | |
423 ar.write(point.y); | |
424 } | |
425 }; | |
426 ``` | |
427 | |
428 ### Return | |
429 | |
430 The preferred style is to return early in case of errors. That makes the code | |
431 more linear and not highly indented. | |
432 | |
433 This code is preferred: | |
434 | |
435 ```cpp | |
436 if (a_condition_is_not_valid) | |
437 return nullptr; | |
438 if (an_other_condition) | |
439 return nullptr; | |
440 | |
441 auto x = std::make_shared<object>(); | |
442 | |
443 x->start(); | |
444 x->save(); | |
445 | |
446 return x; | |
447 ``` | |
448 | |
449 Additional rules: | |
450 | |
451 - Do never put parentheses between the returned value, | |
452 - Do not put a else branch after a return. | |
453 | |
454 ### Auto | |
455 | |
456 We encorage usage of `auto`, it reduces code maintainance as you don't need to | |
457 change your code when your rename types. | |
458 | |
459 ```cpp | |
460 auto it = std::find_if(v.begin(), v.end(), [&] (const auto& obj) { | |
461 return obj.key() == "foo"; | |
462 }); | |
463 | |
464 for (const auto& pair : a_map) | |
465 std::cout << pair.first << " = " << pair.second << std::endl; | |
466 ``` | |
467 | |
468 But do not use `auto` to write code like in python, this is not acceptable: | |
469 | |
470 ```cpp | |
471 auto o = my_object("foo"); | |
472 ``` | |
473 | |
474 ### String views | |
475 | |
476 Use `std::string_view` each time you need a string that you will not own, this | |
477 includes: temporary arguments, return values, compile time constants. | |
478 | |
479 ```cpp | |
480 const std::string_view version("1.0"); | |
481 | |
482 void load(std::string_view id, std::string_view path) | |
483 { | |
484 std::cout << "loading: " << id << " from path: " << path << std::endl; | |
485 } | |
486 ``` | |
487 | |
488 ### Optional values | |
489 | |
490 Use `std::optional` to indicate a null value considered as valid. For example, | |
491 searching a value that may not exist. | |
492 | |
493 ```cpp | |
494 auto find(std::string_view id) -> std::optional<int> | |
495 { | |
496 if (auto it = foo.find(id); it != foo.end()) | |
497 return it->second; | |
498 | |
499 return std::nullopt; | |
500 } | |
501 ``` | |
502 | |
503 ### Avoid definitions in headers | |
504 | |
505 Try to avoid as much as possible function definition in header file. It slow | |
506 down compilation because the compiler has to parse the syntax over and over. | |
507 It's even worse as you may need to recompile a lot of files when you change a | |
508 header rather than a source file. | |
509 | |
510 CMake | |
511 ===== | |
512 | |
513 Style | |
514 ----- | |
515 | |
516 - Try to keep line shorter than 80 columns | |
517 | |
518 ### Spaces | |
519 | |
520 Each programming keyword (e.g. `if`, `foreach`, `while`) requires a single space | |
521 before its argument, otherwise write opening parenthese directly after. | |
522 | |
523 ```cmake | |
524 foreach (c ${COMPONENTS}) | |
525 string(TOUPPER ${c} CMP) | |
526 | |
527 if (${WITH_${CMP}}) | |
528 add_executable(${c} ${c}.cpp) | |
529 endif () | |
530 endforeach () | |
531 ``` | |
532 | |
533 ### Line breaks | |
534 | |
535 When CMake lines goes too long, you should indent arguments at the same level, | |
536 it's also common to see named argument values indented even more. | |
537 | |
538 ```cmake | |
539 set( | |
540 FILES | |
541 ${myapp_SOURCE_DIR}/main.cpp | |
542 ${myapp_SOURCE_DIR}/foo.cpp | |
543 ${myapp_SOURCE_DIR}/bar.cpp | |
544 ) | |
545 | |
546 command_with_lot_of_arguments( | |
547 TARGET foo | |
548 INSTALL On | |
549 SOURCES | |
550 ${myapp_SOURCE_DIR}/main.cpp | |
551 ${myapp_SOURCE_DIR}/foo.cpp | |
552 ${myapp_SOURCE_DIR}/bar.cpp | |
553 COMMENT "Some comment" | |
554 ``` | |
555 | |
556 Modern CMake | |
557 ------------ | |
558 | |
559 CMake evolves over time, if you have read very old articles there is a chance | |
560 that what you have read is actually deprecated and replaced by other features. | |
561 The following list is a short summary of modern CMake features that you must | |
562 use. | |
563 | |
564 ### Imported targets | |
565 | |
566 When they are available, use imported targets rather than plain variables. They | |
567 offer complete dependency tracking with options and include directories as well. | |
568 | |
569 ```cmake | |
570 find_package(Boost COMPONENTS system) | |
571 target_link_libraries(main Boost::system) | |
572 ``` | |
573 | |
574 ### Generator expressions | |
575 | |
576 Use generator expressions when it make sense. For example you should use them | |
577 for variables that are not used at generation time (e.g CMAKE\_BUILD\_TYPE). | |
578 | |
579 ```cmake | |
580 target_include_directories( | |
581 myapp | |
582 $<BUILD_INTERFACE:${myapp_SOURCE_DIR}> | |
583 $<INSTALL_INTERFACE:include> | |
584 ) | |
585 ``` | |
586 | |
587 Warning: do never test against `CMAKE_BUILD_TYPE` in any CMakeLists.txt, IDEs | |
588 like Visual Studio will mismatch what you'll put in the conditions. | |
589 | |
590 ### Avoid global scoping | |
591 | |
592 The following commands must be avoided as much as possible: | |
593 | |
594 - `link_directories`, | |
595 - `link_libraries`, | |
596 - `include_directories`, | |
597 - `add_definitions`. | |
598 | |
599 They pollute the global namespace, all targets defined after these commands will | |
600 be built against those settings. Instead, you should use the per-targets | |
601 commands. | |
602 | |
603 ```cmake | |
604 target_include_directories( | |
605 mylib | |
606 PUBLIC | |
607 $<BUILD_INTERFACE:${mylib_SOURCE_DIR}> | |
608 $<INSTALL_INTERFACE:include> | |
609 ) | |
610 target_link_libraries(mylib foo) | |
611 ``` | |
612 | |
613 ### Defining sources | |
614 | |
615 You MUST never use any kind of `file(GLOB)` commands to list sources for an | |
616 executable. CMake is designed to be re-called by itself only when required, | |
617 having such a construct will not let CMake be able to detect if you have | |
618 added/removed files in your source directory. Instead, you MUST always specify | |
619 all source by hands. | |
620 | |
621 ```cmake | |
622 set( | |
623 FILES | |
624 ${myapp_SOURCE_DIR}/main.cpp | |
625 ${myapp_SOURCE_DIR}/a.cpp | |
626 ${myapp_SOURCE_DIR}/b.cpp | |
627 ) | |
628 | |
629 add_executable(myapp ${FILES}) | |
630 ``` | |
631 | |
632 Markdown | |
633 ======== | |
634 | |
635 Headers | |
636 ------- | |
637 | |
638 For 1st and 2nd level headers, use `===` and `---` delimiters and underline the | |
639 whole title. Otherwise use `###`. | |
640 | |
641 ```markdown | |
642 Top level title | |
643 =============== | |
644 | |
645 Sub title | |
646 --------- | |
647 | |
648 ### Header 3 | |
649 | |
650 #### Header 4 | |
651 | |
652 ##### Header 5 | |
653 | |
654 ###### Header 6 | |
655 ``` | |
656 | |
657 Lists | |
658 ----- | |
659 | |
660 Use hyphens for unordered lists for consistency, do not indent top level lists, | |
661 then indent by two spaces each level | |
662 | |
663 ```markdown | |
664 - unordered list 1 | |
665 - unordered list 2 | |
666 - sub unordered item | |
667 | |
668 1. unordered list 1 | |
669 2. unordered list 2 | |
670 2.1. sub unordered item | |
671 ``` | |
672 | |
673 Code blocks | |
674 ----------- | |
675 | |
676 You can use three backticks and the language specifier or just indent a block by | |
677 for leading spaces if you don't need syntax. | |
678 | |
679 ```cpp | |
680 std::cout << "hello world" << std::endl; | |
681 ``` | |
682 | |
683 And without syntax: | |
684 | |
685 ```markdown | |
686 This is simple code block. | |
687 ``` | |
688 | |
689 Tables | |
690 ------ | |
691 | |
692 Tables are supported and formatted as following: | |
693 | |
694 ```markdown | |
695 | header 1 | header 2 | | |
696 |----------|----------| | |
697 | item 1 | item 2 | | |
698 ``` | |
699 | |
700 Alerts | |
701 ------ | |
702 | |
703 It's possible to prefix a paragraph by one of the following topic, it renders a | |
704 different block depending on the output format: | |
705 | |
706 - Note: | |
707 - Warning: | |
708 - Danger: | |
709 | |
710 Then, if the paragraph is too long, indent the text correctly. | |
711 | |
712 ```markdown | |
713 Note: this is an information block that is too long to fit between the limits so | |
714 it is split and indented. | |
715 ``` |