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