Flow 2.0.0
Flow project: Full implementation reference.
doc-coding_style.cpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/**
19 * @file
20 *
21 * Doc file (meant for reading, not compiling) that formally defines the coding style for the rest of the project.
22 *
23 * @todo There are a few little to-dos inside the Doxygen-skipped main body of doc-coding_style.cpp; check them out.
24 *
25 * @todo A script/tool/something that would auto-scan the code for violations of cosmetic conventions described
26 * in doc-coding_style.cpp. It need not be perfect or comprehensive. Humans tend to have difficulty following lots
27 * of conventions, and it then clutters up code reviews subsequently.
28 *
29 * @todo In all of Flow, scan for the unintentional use of the period character in auto-brief summaries in many doc
30 * headers. A period will be assumed to be the end of the brief summary. We've been disciplined about this, but
31 * I (ygoldfel) just realized that, e.g., "boost.asio" has a period and yet is present in at least some doc headers.
32 * Fix those in some consistent yet palatable way, so that the Doxygen output for those isn't prematurely truncated.
33 * (Check that Doxygen isn't smart enough to figure it out already. If it is, delete this to-do.)
34 */
35
36/* This is basically `#if 0` but fools most syntax highlighters to still treat the body as active C++ to highlight.
37 * `#if 0` often leads to annoying graying out. Also this ensures Doxygen doesn't try to scan it for documentation
38 * to generate. This file is meant to be read straight up (if ideally with syntax highlighting). */
39#ifndef FLOW_DOXYGEN_ONLY
40# ifdef FLOW_DOXYGEN_ONLY
41
42// -- Synopsis / Table of Contents --
43
44/* - About
45 * - Style guide [cosmetic, objective, fairly exhaustive]
46 * - Folder level, namespace basics
47 * - File level
48 * - Basics: Line width; indent; comments; doc header basics
49 * - Identifier formatting
50 * - Order of declarations
51 * - Spacing and indentation
52 * - Misc/loose ends
53 * - #include what you use (do not rely on transitive #include)
54 * - Header files and forwarding; _fwd.hpp pattern
55 * - Exceptions to _fwd.hpp pattern
56 * - _fwd.hpp pattern odds/ends: Macros, constexpr globals
57 * - Best practices [conceptual, subjective, non-exhaustive]
58 * - Comments
59 * - Inlining
60 * - Constructor and initializer invocation
61 * - Naming vis-a-vis units, time units, time math
62 * - Doxygen doc header deep-dive
63 * - Namespaces, libraries
64 * - Error handling
65 * - Logging
66 * - Types and type safety
67 * - General design do's and don'ts */
68
69// -- About --
70
71/* This source file is meant to be read by all coders of Flow (not coders *using* Flow). Do not compile it.
72 * It aims to answer the question: What is the formal style for Flow code (API and internals)? The answer is *slightly*
73 * subtle. The coding style is formally as follows:
74 * - The rules in this file are to be followed, unless there is an excellent, rare reason not to.
75 * - Many rules are not given as explicit rules but by example. E.g., if a code snippet formats a `static`
76 * non-member variable with certain formatting and doc header in this file, then that is to be taken as an
77 * rule that that is how such declarations are to be done.
78 * - Either way, following these rules helps *consistency* as well as hopefully clarity, maintainability, etc.
79 * - If some situation is not specified in this file (either as an explicit rule or by example), and the coder is
80 * asking oneself what this means about how one should proceed, then these are the possibilities:
81 * -# There is no convention to follow. Coder should do what they want while being guided by personal dedication
82 * to clarity, maintainaibility, etc. All that will be lacking, then, is potentially *consistency* with what
83 * some other code snippet did in that situation.
84 * - This certainly happens, but it is worth a little effort to push for consistency and attempt the following
85 * bullet point instead. If that fails, OK, come back here.
86 * -# There is a convention to follow, though not in the present file. Check existing Flow code for how it tends
87 * to be done. If there's a clear answer, copy that.
88 * - In addition, if it appears to be an ironclad 95%+ convention in practice, add it to the present style
89 * doc to eliminate the ambiguity next time.
90 *
91 * Flow is extremely consistent and places a high value on aesthetics, clarity, maintainability, and documentation.
92 * Because it was originally written by one person and with an emphasis on these, it's in a relatively rare state for
93 * a code base of this size: Good style with near-100% consistency is actually achieved. So, let's keep achieving it
94 * in inductive fashion, so to speak.
95 *
96 * Really, it is quite realistic to accomplish this by simply following the rule,
97 * "if in doubt how to present some code, (1) it matters what you decide, and (2) use existing Flow code to guide
98 * you by example." Hence the present file is not *necessary*; or it could contain nothing but the present commentary
99 * and nothing else. Hence the following snippets and comments exist for *convenience* only: so that one can easily
100 * pop on over to here and most times find the desired convention/rule (as opposed to fishing around the existing
101 * code base).
102 *
103 * The rest is split up into 2 groups of guidelines.
104 * - STYLE GUIDE: These are cosmetic rules. They're fairly objective, and to achieve consistency one must follow
105 * them (unless excellent reason to not). Basically this is, like, indentation and ordering and formatting, etc.
106 * - BEST PRACTICES: These are subjective coding style rules. The aim here is no longer mere cosmetic consistency
107 * but the more subjective goals of readability, maintainability, and elegance. These are things like: avoid
108 * `protected` non-`const` data members; prefer iteration to massive recursion; etc. */
109
110// -- STYLE GUIDE: Folder level, namespace basics --
111
112/* For *all* code under `flow` -- even though (as noted in common.hpp) the various namespaces are orthogonal to each
113 * other in functionality -- the following conventions apply. We basically use similar conventions to Boost and
114 * all/most popular STL implementations, plus some additional conventions. To wit:
115 *
116 * - There is a (nearly) 1-to-1 relationship between the tree of `namespace`s and the directory tree.
117 * That is: `flow` is the top level directory and `namespace`; within it are directories X1, X2, ..., each of which
118 * is 1-to-1 to `namespace`s X1, X2, .... This pattern continues recursively as well.
119 * - A potential exception to this is that it may make sense to create a sub-directory, even if doesn't have a
120 * corresponding namespace, from time to time, for special organizational purposes.
121 * - Conversely, a potential exception is namespace X has sub-namespace Y, yet there is no X/Y directory
122 * for stuff from `Y`. We generally recommend against this, except when all of the stuff in `namespace Y`
123 * can be declared in a very small number of headers... namely at most Y.hpp and/or Y_fwd.hpp.
124 * - Since macros (which we generally avoid except where necessary, usually for logging only) do not reside in
125 * `namespace`s, we simulate "macro namespaces" via prefixes. See elsewhere in the present file for details.
126 * - Suppose flow/A/B is a directory corresponding to namespace `flow::A::B`. Then any file of form flow/A/B/x.hpp
127 * must contain *only* symbols in `flow::A::B` *that are intended to be publicly available*. (Of course, function
128 * bodies -- notably function templates -- must sometimes also reside in header files, alongside the interface.)
129 * - Symbols necessary to implement anything else in flow/A/B, but that are *not* intended for public use, must
130 * reside in flow/A/B/detail.
131 * - The user is explicitly not allowed to `include` them directly. However this is not enforced and not
132 * enforceable. (Note: The same is true of all STLs, Boost, and many other products.) (A public header
133 * *may* include a detail/ header if needed, such as when implementing function templates at times;
134 * however the user is by convention not allowed to directly include them.)
135 * - Similarly, the user is explicitly disallowed to reference such symbols directly. Again, this isn't enforced
136 * or (in many cases) enforceable.
137 * - At this time we do not require (or encourage) to segregate such non-public symbols in
138 * `detail` sub-`namespace`s. (Boost/STL sometimes does this; sometimes does not.) Reason is it's annoying to
139 * move stuff between namespaces when making a non-public API public or vice versa; since user isn't meant
140 * to use such things regardless, it is sufficient to merely *indicate* what is public and what isn't; for this
141 * the choice of file/directory is sufficient and no actual `namespace` is required. We may change this policy.
142 * - However: It is typical that a file of form x.hpp requires an x.cpp counterpart. Obviously, this would not
143 * be exported into any public `include/` directory (as it is not a header file). The rule is x.cpp is to be
144 * in the same directory as x.hpp (alongside it).
145 * - In the rare case where a file z.cpp does not have a z.hpp
146 * counterpart, z.cpp should usually reside under detail/ after all. Sometimes it might make sense to place
147 * it near its most-related .hpp file. This particular decision is left to
148 * be made on a case-by-case basis however and shouldn't come up very often. It is also of (relatively) low
149 * importance, as .cpp files are not part of the exported API.
150 * - Headers must end in .hpp. Others must end in .cpp. In the rare case of a C-language-user-supporting header, use
151 * .h. At this time we don't use .inl (and similar) files, but if there's a good reason we might do so, in which
152 * case the convention should be clearly described here.
153 * - Do not use redundant prefixes/postfixes in file names, when the containing directory already contains the same
154 * info -- even if this would create two same-named files at different places in the directory tree.
155 * E.g., if you have D/node.hpp and D/asio/node.hpp, that's fine; don't name the latter D/asio/asio_node.hpp.
156 * - For that matter: follow the same guideline when naming types, functions, and so on. I.e.,
157 * don't have asio::Asio_node; just asio::Node. Yes, this may require disambiguation (by more fully qualifying
158 * identifiers at times) in code. Nevertheless. We must take a stand! */
159
160// -- STYLE GUIDE: File level --
161
162/// @file
163#pragma once
164
165/* - Every source file begins with @file, so that Doxygen will add it to its Files page in the generated docs.
166 * - Every header then begins with that `pragma` to avoid redefinitions from multiple `#include`s.
167 *
168 * - File names are to be all-lower-case words, underscore-separated.
169 * - @todo Defining formal rules beyond that feels tedious... but technically we should.
170 * Until then follow the many existing examples.
171 * - Header file name ends with .hpp.
172 * - .h if file is intended as a C API (even if supports C++ use also). This is rare.
173 * - Translation unit names ends with .cpp. */
174
175// This illustrates how to properly indent #directives.
176#ifdef FLOW_SOME_DEFINE
177# if FLOW_SOME_OTHER_DEFINE
178# include "conditionally_included_file.hpp"
179# endif
180#endif
181// The indentation of #directives is 100% orthogonal to indentation of actual code, as this shows:
182void f(bool flag)
183{
184 if (flag)
185 {
186#if FLOW_YET_ANOTHER_DEFINE
187 do_something_conditionally_compiled();
188#endif
189 }
190}
191/* Also, use the spirit of the rules for parenthesizing expressions to express priority even when unnecessary,
192 * which are described elsewhere in this guide, not just for actual code expressions but also within #if directives.
193 * There is however no need to surround the entire thing in (parentheses), as a regular `if ()` forces it, while
194 * #if does not. */
195#if defined(FLOW_THING) && ((!defined(FLOW_OTHER_THING)) || defined(FLOW_THIRD_THING))
196 // [...]
197#endif
198
199// -- STYLE GUIDE: Line width; indent; comments; doc header basics --
200
201/* - Lines are up to 120 columns, inclusive. [Try <110 to leave space for later edits.]
202 * - One indent level = 2 spaces. No tab characters.
203 * - Comments should be composed of sentence-ish things. Each sentence starts with a Capital and ends with a period
204 * or similar. [Optional: Use two spaces between sentence-ish things. Many find this annoying; hence optional.]
205 *
206 * Multi-line comments use C-style comment markers like this. */
207
208// Single-line comments are C++-style like this.
209
210/**
211 * - Comments that start with 2 asterisks are *doc headers* and are understood by Doxygen to be documenting the entity
212 * immediately following. Essentially *every* entity not inside the {braces} of a function -- files, macros,
213 * `class/struct/union/enum`s, functions, variables/constants, namespaces, aliases, ??? -- *must*
214 * have a doc header. In this case we're documenting a `class`. When documenting a function, always document
215 * its body-less prototype only.
216 * - We explain Doxygen details elsewhere in this file though.
217 * - Do *not* place any doc headers inside {braces} of a function, such as for local variables. Just comment, or not,
218 * as you see fit for clarity/etc.
219 *
220 * - Note doc headers have 1 extra leading and 1 trailing lines vs. non-Doxygen comments, as seen in this block.
221 */
222class Cool_class
223{
224 // [...]
225
226 /// Single-line *doc headers* are relatively rare but allowed and start with 3 slashes instead of 2.
227 static const unsigned int S_COOL_CONSTANT_BEING_DOCUMENTED_WITH_A_ONE_LINE_DOC_HEADER;
228 //< BAD: Don't use trailing doc headers like this. [Rationale: Why add inconsistency?]
229
230 // [...]
231}; // class Cool_class
232/* ^-- Closing {braces} and possibly (parentheses) spanning screenfuls should usually feature a terminating comment
233 * like that `// class Cool_class` one. Typical conventions for how these end-comments look can be found across
234 * Flow by example. This style helps readability (but may make maintenance somewhat more annoying). */
235
236// -- STYLE GUIDE: Identifier formatting --
237
238/* Words-separated-by-underscores formatting is to be used by ALL identifers!
239 * - Do not use CamelCase or any variation.
240 * - Do not use wordsmooshing. Is it two words? Yes? Then put an underscore _ between them. Period!
241 *
242 * [Rationale: This decision to forego CamelCase in favor of underscores does not imply the latter is superior
243 * in and of itself. A case can be made for either approach, certainly with pros and cons for each.
244 * The deciding factor for choosing underscores for Flow originally was that the STL *and* Boost ecosystems
245 * so heavily affected the project that it seemed strange to intentionally allow the visual clashing between
246 * STL/Boost identifiers and Flow ones when seen side-by-side in real code (in and out of Flow itself).
247 *
248 * A certain large code project used CamelCase and thus provided a counterpoint. However, even that unnamed
249 * project had tons of core code that used under_score_style after all; and there's plenty of wordsmooshing too.
250 * So if it's not even consistent in the first place... meh. We do what's best.] */
251
252/* - Every identifier consists of a root; a possible prefix (s_, m_, S_); and a possible suffix (_t).
253 * - The root = always 1+ words, separated by underscores if 2+.
254 * - Each words consists of ASCII letters and numbers only. The first word in root must start with letter.
255 *
256 * - Most roots are all-lower-case: */
257word;
258two_words;
259three_or_4_words;
260identifier_version1_or_v2;
261acronyms_are_just_words_eg_url;
262wordsmooshing_abbrevs_like_uint_are_ok_if_very_well_known;
263
264// However, compound types must start with a single Capital letter but otherwise all-lower-case:
265class First_letter_of_a_class_is_capital_while_rest_always_lower_case;
266struct Same_format_for_struct;
267union Same_format_for_union;
268enum Same_format_for_old_enum; // Non-`class` `enum` is considered a compound type for our purposes.
269enum class Same_format_for_new_enum; // In any case always use `enum class` if possible, not plain `enum`.
270using Typedefs_thereof_as_well = /* [anything resolving to a compound type] */;
271// However, if an alias is to a non-compound type -- typically integers, etc. -- do *not* capitalize but add _t suffix:
272using uint8_t = unsigned char;
273using ptr_diff_t = signed long long;
274/* BAD: Do not use `typedef`.
275 * Use `using` (for consistency + its extra features, esp. template parameterization, shown just below).
276 * typedef signed long long ptr_diff_t;
277 * typedef [anything resolving to a compound type] Typedefs_thereof_as_well; */
278template<typename Param_type>
279using Parameterized_alias
280 = /* [anything resolving to a compound type, with Param_type as part of this alias definition] */;
281// Now one can use Parameterized_alias<X> for any X.
282
283// Most roots are lower-case (first letter aside possibly), but a couple of things must use ALL-CAPS:
284// `#define`d symbols:
285#define MACROS_ARE_IN_CAPS /* [...] */
286#define FUNC_MACROS_ARE_2(/* [...] */) /* [...] */
287// Constants in func body:
288{
289 static const int STATIC_CONSTANT = /* [...] */;
290 const float LOCAL_NON_STATIC_CONSTANT = 2.3; // Value known at compile time => consider it a constant => all caps.
291 // Value not straightforwardly known at compile time => not a "constant" in this context => all-lower-case after all.
292 const auto& blah_ref = *blah_ptr_arg;
293}
294enum class /* [...] */
295// or: enum /* [...] */
296{
297 S_ENUM_VALUES_ARE_CONSTANTS_TOO, // S_ prefix explained below.
298 S_ENUM_VALUES_ARE_CONSTANTS_3 = 3
299};
300
301/* Class/struct template `typename` identifiers look like class/struct/whatever types.
302 * - Having an alias { inside } class/struct is preferred for generic programming + other reasons.
303 * If so, preferred (not absolutely mandatory) style is below (`_t` versus no `_t`).
304 * tparam constants looks like local constants.
305 * - Having an alias { inside } class/struct is preferred also for generic programming + debugger + other.
306 * If so, mandatory style is is below (lead alias identifier but not tparam with `S_` + `static constexpr`). */
307template<typename Type_ay, typename Type_bee_t, bool CONSTANT_AY, bool CONSTANT_BEE>
308{
309public:
310 /// Alias or short-hand for generic programming.
311 using Type_bee = Type_bee_t;
312 /// Constant mirroring template parameter.
313 static constexpr S_CONSTANT_BEE = CONSTANT_BEE;
314}
315/* Function templates: similar for types (but: no particular en/discouragement of aliases).
316 * Function templates: similar for constants but no `static` or `S_` (and: no en/discouragement of aliases). */
317template<typename Type_ay, typename Type_bee_t, bool CONSTANT_AY, bool CONSTANT_BEE>
318void do_cool_thing(Type_bee_t thing, bool value = CONSTANT_BEE)
319{
320 using Type_bee = Type_bee_t; // Allowed if you feel like it.
321 constexpr auto SHORT_B = CONSTANT_BEE; // Ditto.
322}
323
324/* `typename` identifiers look like class/struct/whatever types.
325 * - Having an alias { inside } class/struct is preferred for generic programming + other reasons.
326 * If so, preferred (not absolutely mandatory) style is below (`_t` versus no `_t`).
327 * tparam constants looks like local constants.
328 * - Having an alias { inside } class/struct is preferred also for generic programming + debugger + other.
329 * If so, preferred (not absolutely mandatory) style is: end `typename` identifier with `_t`, alias without.
330 */
331
332// So, those are the roots. What about prefixes and postfixes? Simple:
333
334// Only one postfix exists: _t, used for aliases of non-compound types. See 2 examples above.
335// Three prefixes exist: s_, m_, S_:
336
337/* Anything `static` must be prefixed with s_ or S_ depending on capitalization chosen above for the root.
338 * That includes file-level, compound member, even local `static`s; plus `enum` members (technically not `static`). */
339static Cool_class s_singleton_instance; // Not a constant.
340static const std::string S_STRING_VAR; // Constant.
341// `enum` values are constants. They must be prefixed with S_ (see `S_ENUM_VALUES_ARE_CONSTANTS_3` above).
342
343/* Any non-static member variable of a compound type, a/k/a *data member*, must be prefixed with m_.
344 * Since a member constant is by definition `static`, there is no M_ prefix but an S_ prefix.
345 * A static member variable has s_ prefix.
346 * A local variable, or a local constant, carries no prefix.
347 * A global non-member non-static constant (probably rare) carries no prefix.
348 * Same with global non-member non-static variable (even rarer). */
349extern int no_prefix_because_not_a_member_nor_static; // .hpp
350extern int NO_PREFIX_BECAUSE_NOT_A_MEMBER_NOR_STATIC; // .hpp
351int no_prefix_because_not_a_member_nor_static; // .cpp
352int NO_PREFIX_BECAUSE_NOT_A_MEMBER_NOR_STATIC; // .cpp
353class Some_class
354{
355 // [...]
356 const Some_class& m_data_member_is_a_ref;
357 Some_enum m_enum_data_member;
358 static const Some_type S_CONSTANT_MEMBER;
359 static constexpr int S_CONSTANT_MEMBER = 32;
360 static Some_type s_variable_member;
361 void some_func()
362 {
363 int no_prefix_because_not_a_member;
364 const int NO_PREFIX_BECAUSE_NOT_A_MEMBER = 2;
365 constexpr int NO_PREFIX_BECAUSE_NOT_A_MEMBER = 3;
366 // [...]
367 }
368 // [...]
369}
370
371// -- STYLE GUIDE: Order of declarations --
372
373/* Flow style cares about ordering things consistently and readably, more than usual in my opinion.
374 *
375 * - Basic principle: Whenever an API and implementation for anything is written -- including APIs used only
376 * internally -- the API (black box) must come first; implementation (white box) second. In other words,
377 * when *declaring*, start with public, end with private (protected in middle if applicable).
378 * [Rationale: User of entity X cares how to use X, not what's inside X -- ideally. So the public parts come first.
379 * Plus it adds consistency.]
380 * - The function *bodies* need not follow any such order, so just do what feels right.
381 * - Basic principle: Whenever in some section of an API or implementation (notably a `public:` or `private:` section
382 * of a `class` or `struct`), follow the same consistent order of grouped items:
383 * - The order is: types, constructors/destructor, functions (including `operator`s), constants (i.e., all-caps
384 * things), data members. [Rationale: Roughly speaking it follows a black box->white box ordering.]
385 * (`static` or otherwise).
386 * - However, data-store `struct`s should place constructors/destructor and functions at the bottom instead.
387 * - Basic principle: There is a standard order of items in each file but especially for headers .hpp.
388 * Follow the order in the snippets below.
389 * - Basic principle: Human nature: The ordering guidelines are easy to forget to follow. To encourage it, label
390 * each grouping with a standard comment (e.g., "// Constructors/destructor.", "// Data.", etc.). Snippets below.
391 * - Do NOT have "empty" such headers (e.g., if there are no data members then no "// Data." comment either).
392 * - Basic principle: Exceptions to these guidelines will happen and aren't the end of the world at all. */
393
394// order.hpp follows: Illustrates above principles/shows proper ordering by example, in a header file:
395
396/// @file
397#pragma once
398
399#include "own_headers_included_first.hpp"
400#include <boost/higher_level_angly_headers_go_next.hpp>
401#include <vector> // STL- and lower-level includes typically bring up the rear.
402
403#if 0
404/* As prescribed just above, precede groupings of like things with these standard, single-line comments like:
405 * Again, this helps maintain discipline and set a good example.
406 * Reiterate: DO omit such a heading when that "section" is empty. */
407#endif
408
409// Types.
410
411/// Short-hand for `boost::any`. Any ordering is fine within the //Types grouping.
412using Cool_type = boost::any;
413
414/**
415 * A class that accomplishes so-and-so.
416 * Similarly with structs, unions, whatever.
417 */
418class Cool_class
419{
420public:
421 // [...Follow order as shown in private: below....]
422protected:
423 // [...Follow order as shown in private: below....]
424private:
425 // Friends.
426
427 /**
428 * Server_socket must be able to forward `accept()`, etc. to Cool_class.
429 * This documents a `friend` relationship.
430 *
431 * Can also have function `friend`s here, though they don't require official doc headers.
432 */
433 friend class Server_socket;
434
435 // Types.
436
437 /// Short-hand for ref-counted pointer to this class.
438 using Ptr = boost::shared_ptr<Cool_class>;
439
440 /// An inner class (struct, union, enum, enum class, etc.). Declare its body outside whenever possible!
441 struct Inner_class_of_cool;
442
443 // Constructors/destructor.
444
445 /// Constructs the object.
446 Cool_class();
447
448 /**
449 * Copies object into newly created `*this`.
450 * @param src
451 * Source.
452 */
453 Cool_class(const Cool_class& src);
454
455 /**
456 * Explicitly deleted (possibly would-have-been-auto-generated) ctors and operators are so declared.
457 * @param src
458 * Source.
459 */
460 Cool_class(Cool_class&&) = delete;
461
462 /// Boring destructor.
463 virtual ~Cool_class();
464
465 // Methods.
466
467 /**
468 * Copies object `src` onto `*this`.
469 * Operators are considered just methods in this context but tend to come first due to `operator=()` being
470 * conveniently placed near copy constructors and similar.
471 *
472 * @param src
473 * Source.
474 */
475 Cool_class& operator=(const Cool_class& src)
476
477 /**
478 * A regular method.
479 *
480 * Prototype only! Template and inline function bodies MUST be outside class body (see below).
481 */
482 void cool_method();
483 /* ^-- BTW, no m_ (or other prefix) to indicate a method is private.
484 * [Rationale: Some like such a convention, but in my experience it's too easy to forget and a huge pain to maintain,
485 * as public things often become private and vice versa... no one wants to do that renaming. Goes bad even with good
486 * intentions in my experience.] */
487
488 /**
489 * A method template.
490 * @tparam Some_type
491 * Document any template parameters.
492 */
493 template<typename Some_type>
494 Some_type cool_template_method();
495
496#if 0
497/* If this is a pure data-store `struct`, ideally move the below (constants/data) above
498 * constructors/destructor/functions, as in that case the data are the "star." */
499#endif
500
501 // Constants.
502
503 /**
504 * Constants formally come before other data; and are always static.
505 * Reminder: If it doesn't have a value straightforwardly known at compile time, it's not a constant and not
506 * upper-case.
507 */
508 static const float S_PI;
509
510 // Data.
511
512 /// Carefully document each data member. Same as above but for not-constants.
513 Inner_class_of_cool* m_cool_ptr;
514}; // class Cool_class
515
516#if 0
517/* More types (classes/structs/unions/`using` aliases/enums/enum classes) continue here if needed; including inner
518 * classes.
519 *
520 * - However, leave private inner classes (like Cool_class::Inner_class_of_cool) until the end, ideally, following
521 * the aforementioned basic principle: API first, implementation second. */
522#endif
523
524/**
525 * @private
526 *
527 * The body of an inner class should be kept outside of its containing class, for readability.
528 * [Sometimes C++ circular reference rules make this hard or impossible, so exceptions to this are acceptable.]
529 *
530 * Use @private as shown above *if and only if* the inner class is private. This is due to a Doxygen quirk
531 * (arguably bug).
532 */
533struct Cool_class::Inner_class_of_cool
534{
535 // [...same order of stuff as shown in Cool_class]
536};
537
538// Free functions.
539
540/**
541 * Free functions aren't super-common but definitely legitimate and go in this area. Prototype only!
542 *
543 * @param c1
544 * First operand.
545 * @param c2
546 * Second operand.
547 * @return New object that sums the operands.
548 */
549Cool_class operator+(Cool_class c1, Cool_class c2);
550
551/**
552 * Output of Cool_class `c` to a `basic_ostream`.
553 * This is another free function, but this one is a template. They can go in any order though.
554 *
555 * @tparam Ostream
556 * Document the template param type.
557 * @param os
558 * Stream to write to.
559 * @param c
560 * Object to write.
561 * @return `os`.
562 */
563template<typename Ostream>
564Ostream& operator<<(Ostream& os, Cool_class c);
565
566// Constants.
567
568/// The mathematical `e`. Non-member constants are fairly rare but would go in this area.
569extern const float S_EXPONENT;
570
571// Data.
572
573/// Non-member non-constant/variable: these are arguably even more rare but would go here.
574extern Cool_class s_singleton_instance;
575
576#if 0
577/* Everything above was the API! The separation helps readability, as implementation details come later to the extent
578 * possible. This isn't 100% true: `private` isn't API; `private` inner classes aren't API. Still, pretty close. */
579#endif
580
581// Template/constexpr/inline implementations.
582// [We actually discourage explicit inlining elsewhere, but if needed they go here.]
583
584#if 0
585/* Finally the "actual code" (function bodies) go here! Recall, do NOT duplicate doc headers.
586 * *Every* body *must* have a prototype present somewhere earlier: class/etc. methods inside class{},
587 * free functions outside. Then all the bodies go either here (templates, inline) or in .cpp counterpart (others). */
588#endif
589
590template<typename Ostream>
591Ostream& operator<<(Ostream& os, Cool_class c)
592{
593 // [...] This is a free function, but member functions (methods) go in this area as well:
594}
595
596template<typename Some_type>
597Some_type Cool_class::cool_template_method()
598{
599 // [...]
600}
601
602// order.cpp follows: Illustrates above principles/shows proper ordering by example, in a counteraprt to header file:
603
604/// @file
605
606#include "[...]/order.hpp"
607// [...includes...]
608
609#if 0
610/* We forego tediously describing the ordering in here. In general, because this is not the API (unlike in .hpp),
611 * things are more lax in here.
612 *
613 * - Still precede each grouping of stuff with headers.
614 * - Typically, you will only have the following, sans doc headers (see below):
615 * - // Static initializers.
616 * - // Implementations.
617 *
618 * Nevertheless, there are many other things that might be necessary less commonly. So I just list everything I can
619 * think of, below; copy/paste into your .cpp file perhaps; delete what's not relevant to you; and proceed to fill them
620 * out.
621 */
622#endif
623
624// Static initializers. [Common: Initializations of constants, static data, etc. declared/documented in .hpp.]
625
626// Local types. [Rare: Types used only in this .cpp. This is rare in C++, but who knows?]
627
628// Local functions. [Rare: Prototypes *and doc headers* ONLY! `static` non-member functions are rare in C++.]
629
630// Local constants and their initializers. [Rare: Constants used only in this .cpp. Again, rare in C++.]
631
632// Local data and their initializers. [Ditto.]
633
634// Implementations. [Common: The bodies of functions declared in .hpp and (uncommonly) local ones from just above.]
635#if 0
636/* It also helps, when there are 2+ classes/structs/etc. with bodies, to have a separate section for each:
637 * // Cool_class implementations.
638 * // Cool_class::Inner_class_of_cool implementations.
639 * It's left to your discretion however. */
640#endif
641
642// -- STYLE GUIDE: Spacing and indentation --
643
644/* Beyond the basics (no tab characters; 120 columns per line; indent level = 2) there are various conventions
645 * regarding white-space and indentation. Many style guides omit some of these details, and inconsistency is common.
646 * Please follow the following rules as shown by example. */
647
648namespace flow::submodule
649{
650
651/* A new { block } *always* means new indentation level, *except* due to being within namespace {}.
652 * Nested namespaces themselves shall use C++17 syntax, wherein one writes `namespace a::b::c` instead of
653 * `namespace a { namespace b { namespace c`. This is shown above.
654 * (Even if 1 file declares members of 2+ different namespaces, one shall declare each namespace, no matter its
655 * depth level, using `namespace a::b::c` syntax, where `a` is always an outer namespace.)
656 * The actual members declared within a given namespace shall begin indentation at column 0, as seen in the following
657 * `cool_method()` declaration.
658 * [Rationale: The latter avoids losing one indent-level of real estate on practically every line of code with little
659 * benefit.
660 * @todo Since we are now on C++17, and hence mandate the use of `namespace a::b::c {}` syntax, consider updating style
661 * guide -- and all code -- to indent properly even inside namespaces, while continuing to demand the use of
662 * `namespace a::b::c` syntax. One level on indent shall be lost by this, but since with the C++17 syntax it *is*
663 * at most just the 1 indent-level -- as opposed to typically 2 or more before C++17 syntax -- the prettier and more
664 * consistent-overall indentation may be worth it.] */
665
666void Cool_class::cool_method(int a, int b)
667{
668 if (a < b)
669 {
670 /* *Always* use {} around one-statement flow-control bodies even when unnecessary, such as here.
671 * [Rationale: Doing otherwise leads to frequent maintenance bugs. Also this helps simplify rule set.] */
672 return;
673 }
674
675 // else
676 // [^-- Little markers like this are common in Flow. Not mandatory but consider doing same.]
677
678 /* - *Always* place spaces around binary/ternary operators (`a * b`, `?:`), never in front of unary ones (`-a`).
679 * - *Always* use `()` to enforce order of operations, even when redundant to build-in language rules!
680 * [Rationale: It hugely helps avoid bugs. It also helps reassure reader of the coder's intent.]
681 * - Exception: The `.`, `->`, `*`, `&` operators. They're so high-priority that there's really no need.
682 * Same for function calls `func()`.
683 * - Exception: The `=` and other assignment ops. They're so low-priority that there's really no need.
684 * - If an expression is long or complicated, use your judgment to optionally make it into multiple lines for clarity.
685 * - Use column-lined-up intra-expression indentation as shown here.
686 * - If an operator (in this case `?` and `:` and `&&`) can go either at the end of line N or start of line (N + 1),
687 * prefer the latter.
688 * [Rationale: There are tedious pros/cons. I chose this for consistency, but the other way would've been OK.] */
689 a = b = (m_other_flag || (m_flag
690 && (((-a) * rnd()) > 3)))
691 ? (m_val1.m_sub_val + cool_func1())
692 : (m_val2_ptr->m_sub_val + cool_func2);
693
694 /* A separate `statement;` per stack variable declaration, even when you can combine them.
695 * [Rationale: `int* a, b;` misleads one into thinking `b` is a pointer like `a`. And simplicity+consistency.] */
696 int* a;
697 int b;
698 float& c = m_floatie; // The type is `float&`; the value is `c`. Do NOT do: `float &c;`.
699 float* d = m_ptr; // Same. Type is `float*` (not `float *`); value is `d`.
700
701 // `const` ordering in type names:
702 const char* str; // This is fine; it's so typical out in the world that we couldn't possibly disallow it.
703 char const * str; // This is arguably better; reading backwards is useful: pointer to / const / char.
704 // What if you want the pointed-to value to be immutable AND the pointer *itself*?
705 char const * const str; // Read R2L: const / pointer to / const / char.
706#if 0 // Don't do this when it gets complex with multiple consts; use the R2L ordering to avoid confusion (prev line).
707 const char * const str; // Don't do this.
708#endif
709
710 /* Logging is very common and has some tiny subtleties to improve readability.
711 * Intra-message indentation is to be avoided. [Rationale: Just cosmetics. It looks nicer.] */
712 FLOW_LOG_WARNING("In logging/diagnostics/etc. place [" << a << "] around variables. "
713 "Like comments, use sentence-like things, start with capital, end with period/etc. and 1-2 spaces "
714 "unless end of the message. Optional but highly encouraged.");
715 // But when there are 2+ arguments to the call, add intra-arg indentation back in.
717 (Sev::S_WARNING,
718 "Async_file_logger [" << this << "] @ [" << m_log_path << "]: do_log() just tried to log msg when file stream "
719 "was in good state, but now file stream is in bad state, probably because the write failed. "
720 "Giving up on that log message. Will retry file stream for next message.");
721 // ^-- Note indentation in 2nd arg, so it's clear at glance there are 2 args and not 4.
722
723 // Personal discretion about when to break-and-indent, vs. when to line up by column.
724 FLOW_LOG_WITHOUT_CHECKING(Sev::S_WARNING, "Some message with stuff in it."); // Cool.
725 FLOW_LOG_WITHOUT_CHECKING(Sev::S_WARNING,
726 "Some message with stuff in it."); // Cool as well.
728 (Sev::S_WARNING, "Some message with stuff in it."); // Perfectly cool also.
730 (Sev::S_WARNING,
731 "Some message with stuff in it."); // Excessive here (since it's short), probably, but it's also fine.
732
733 /* Finally, when assembling long strings (such as for logging, or inside FLOW_LOG_...()) via `ostream`,
734 * it is OK to somewhat break other conventions for readability. To quote real code: */
735 os
736 <<
737 "--- Basic socket state ---\n"
738 "Internal state: [" << m_int_state_str << "].\n"
739 "Client or server: [" << (m_is_active_connect ? "client" : "server") << "].\n"
740 // Also visible below in the options but show it separately for convenience.
741 "Reliability mode: [" << (m_sock_opts.m_st_rexmit_on ? "reliable/rexmit on" : "unreliable/rexmit off") << "].\n"
742 "--- Buffers/queues/lists ----\n"
743 "Receive window (free space): [" << m_rcv_wnd << "]\n"
744 " = limit\n"
745 " - Receive buffer size: [" << m_rcv_buf_size << "]\n"
746 " - reassembly queue total data size: [" << m_rcv_reassembly_q_data_size << "] (0 if rexmit off)\n"
747 " " << (m_sock_opts.m_st_rexmit_on ? "Reassembly queue" : "Dupe check list") <<
748 " length: [" << m_rcv_packets_with_gaps << "].\n"
749 " Last advertised: [" << m_rcv_wnd_last_advertised << "].\n";
750 /* ^-- Note that this combines compiler-concatenated string-literals and trailing << (the latter being discouraged
751 * elsewhere in this guide) for readability, so the final output is somewhat easier to visualize. */
752
753 /* Lambdas are HEAVILY used yet very unusual-looking vs. rest of C++. Formatting decisions can be challenging.
754 * Use the following conventions. */
755
756 /* A lambda can (typically SHOULD NOT but use discretion!) begin anywhere, in any expression, even though it
757 * represents a function/closure thing. In this case it begins smack in the middle of a regular function call inside
758 * another function call.
759 * - Always include a [capture section], even if empty.
760 * - Always include an (args list), even if empty.
761 * - Always include an explicit `-> type` -- even if it can be auto-figured-out -- on same line as (args list).
762 * - Except if the type would have been `void`; then omit it.
763 *
764 * [Rationale: It's quite subjective, but I've (ygoldfel) found this leads to visual pattern recognition of a lambda;
765 * yet without excessive amounts of boiler-plate characters. Plus, it creates discipline to carefully craft
766 * each lambda as opposed to "shooting from the hip" as scripting languages like JavaScript encourage.] */
767 sched_task
768 = util::schedule_task_at(get_logger(), wait_until, &task_engine,
769 m_make_serial.wrap // Lambda begins as the arg to this function call:
770 ([this, timeout_state, on_result, would_block_ret_val, event_set]
771 (bool) -> bool
772 {
773 /* *Main point*: The lambda's { body } *always* starts on the same column as the current *statement*.
774 * This is regardless of there the lambda itself -- the [captures] -- started.
775 * [Rationale: This preserves a sane indentation level! A { body } that starts on some column in the middle of
776 * a line is just a nightmare of inconsistency and maintenance.] */
777
778 FLOW_LOG_TRACE("[User event loop] "
779 "Timeout fired for async op [" << event_set << "]; clean up and report to user.");
780
781 Error_code dummy_prevents_throw;
782 event_set->async_wait_finish(&dummy_prevents_throw);
783
784 return true; // Always use an explicit `return x;`, if you must return a value. No funny lambda business.
785 })); // timer.async_wait() callback.
786
787#if 0 // NO. Do not start the body lined up like this. Line it up with the statement, as shown above.
788 sched_task
789 = util::schedule_task_at(get_logger(), wait_until, &task_engine,
790 m_make_serial.wrap // Lambda begins as the arg to this function call:
791 ([this, timeout_state, on_result, would_block_ret_val, event_set]
792 (bool) -> bool
793 {
794 // [...Body...]
795 })); // timer.async_wait() callback.
796#endif
797
798 // You may put lambda { body } on same line as the rest of it -- for VERY short bodies like this.
799 func([this](){ hooray(m_data_member); });
800
801 // While in-line lambdas are allowed (as just above), they *must* then be the last part of the statement.
802
803 func(1, 2, [this](){ hooray(m_data_member); }); // A bit ugly arguably, but this is allowed.
804#if 0 // NOT allowed. There is more "stuff" after the lambda, so don't in-line it.
805 func([this](){ return m_data_member; }, 1, 2);
806#endif
807 // In that case save it in a variable.
808 const auto lambda = [this](){ hooray(m_data_member); };
809 func(lambda, 1, 2);
810
811 /* To be clear, in general, if an in-line lambda is looking even a little obnoxious, err on the side of
812 * "just save it in a `const auto`," as shown above. */
813} // Cool_class::cool_method() [<-- Note the convention for terminating `}` comment of non-tiny function bodies.]
814
815/* [Optional but strongly encouraged:] If a function takes 1 or more function-style arguments (which
816 * in practice end up being created from lambdas, usually), put them at the end of the function's arg list.
817 * [Rationale: It helps make it possible to in-line lambdas in most situations, if desired.] */
818void Cool_class::cool_method2(log::Logger* logger, const flow::Function<void ()>& on_completion_func)
819{
820 // [...]
821 cool_method2(logger, [this]()
822 {
823 hooray(m_data_member);
824 }); // Can always in-line it, because it's the last arg to cool_method2(), so nothing can follow it.
825 // [...]
826}
827
828} // namespace flow::submodule
829
830// Label-type thingies (ctor init; public/private/protected; `case X:`/`default:`; the rare actual goto label):
831class X
832{
833public: // Label starts on same column as preceding thing.
834 X();
835
836private: // Ditto.
837 C m_data_member1;
838 int m_data_member2;
839};
840
841X::X() : // ATTN: initializers start on next line and indented to match the { body }.
842 m_data_member1(rnd()),
843 m_data_member2(0)
844{
845 switch (/* [...] */) // @todo This is the current convention, but it's kinda ugly TBH. Consider adding indentation.
846 {
847 case 0: // Label again starts on same column as preceding thing.
848 do_something0();
849 break;
850 case 1:
851 { // If you have declarations, use a { block } to segregate it;
852 auto x = rnd();
853 do_something1(x);
854 break;
855 }
856 default:
857 goto get_out;
858 }
859
860 // Alternative `switch` formatting, when all case bodies are single-statement, short returns:
861 switch (op_result())
862 {
863 case Xfer_op_result::S_FULLY_XFERRED: return "FULLY_XFERRED";
864 case Xfer_op_result::S_PARTIALLY_XFERRED: return "PARTIALLY_XFERRED";
865 case Xfer_op_result::S_ERROR: return "ERROR";
866 default: assert(false);
867 }
868
869get_out: // A rare actual label.
870} // X::X()
871
872// -- STYLE GUIDE: Misc/loose ends --
873
874#if 0 // Do not do file-scope `using`. [Rationale: It's criminal in .hpp; arguably obnoxious in long `.cpp`s.]
875using namespace std; // Just, no.
876using std::string; // More defensible, in .cpp, but we still don't do it.
877#endif
878
879// However, `using` (not `using namespace`) is ubiquitous at function scope.
880void Cool_class::some_method()
881{
882#if 0
883 using namespace std; // Still, just, no.
884#endif
885 /* It is allowed and *strongly encouraged* [but optional] to avoid all/most namespace `qualifiers::`
886 * by pre-qualifying them via `using` and namespace aliases.
887 * - Always at the top of function. Almost never in a lower scope.
888 * - Types and functions can be thus `using`ed.
889 * - Namespace aliases can go here too. */
890 namespace util = flow::log::util;
891 using std::string;
892 using util::cool_util_method;
893
894 // 2 things are pre-qualified above; 1 thing is explicitly qualified. The latter is usually avoided but is OK.
895 string x = cool_util_method() + util::some_free_function();
896}
897
898// A bunch of subtleties when using complicated C++11 initializers, so just kinda follow some examples:
899const boost::unordered_map<Event_set::Event_type, Event_set::Func_ptr>
900 // @todo It's a bit ambiguous what the indentation "anchor" column should be here....
901 Event_set::S_EV_TYPE_TO_IS_ACTIVE_NODE_MTD
902 // Use judgment to either have {} innards indented on separate line(s) or in-line. Examples of both:
903 {{
904 { Event_set::Event_type::S_PEER_SOCKET_READABLE, &Node::sock_is_readable },
905 { Event_set::Event_type::S_PEER_SOCKET_WRITABLE, &Node::sock_is_writable },
906 { Event_set::Event_type::S_SERVER_SOCKET_ACCEPTABLE, &Node::serv_is_acceptable }
907 }};
908// See also "Constructor and initializer invocation" under BEST PRACTICES.
909
910// Misc. convention:
911T* x = nullptr; // This is the modern Flow convention.
912T* x = 0; // OK and may be seen due to Flow originating before nullptr existed. Ideally change to nullptr on sight.
913// Note, no `NULL`! NULL is C stuff; no need.
914
915// -- STYLE GUIDE: #include what you use (do not rely on transitive #include) --
916
917/* Highly encouraged (follow it in new code; retrofit older code opportunistically, when already editing it):
918 * A file shall directly `#include` the header for each external symbol it uses -- Flow, Boost, STL, etc. -- even if
919 * that header is known to arrive anyway, transitively, via another `#include`.
920 *
921 * Rationale: A transitive include is an accident of the current include graph. When some intermediate header is
922 * slimmed down (which we do, for one thing, for compile times; see the _fwd.hpp pattern below), its dependents
923 * break -- or, worse, keep compiling only on certain compilers or standard library versions. Moreover, direct
924 * includes document what a file actually depends on.
925 *
926 * - "The header for the symbol" means the one declaring it to the degree you need: its _fwd.hpp, if a
927 * forward-declaration suffices; else its full header. (See the _fwd.hpp pattern below.)
928 * - Exception: If a header is designated as *the* way to obtain some facility, include that one instead of the
929 * lower-level header(s) it wraps. E.g., boost.chrono (types and `ostream<<` I/O) shall come via
930 * "flow/common.hpp", never by including <boost/chrono.hpp> (et al) directly; see the comments atop
931 * flow/common.hpp for why.
932 * - Common sense: Sometimes headers are basically collections of `#include`s of other headers; Boost does this
933 * a whole lot. It is of course fine to #include the the collection instead of individual header(s) it contains,
934 * if you feel it's better, all else being equal; this rule is not about that. */
935
936#include "flow/util/util.hpp" // Yes: Suppose this file uses flow::util::round_to_multiple()...
937#include "flow/common.hpp" // ...even if (in this hypothetical) this one already includes the above.
938
939// -- STYLE GUIDE: Header files and forwarding; _fwd.hpp pattern --
940
941/* Flow now follows the forwarding-header pattern observed in various Boost libraries, although I (ygoldfel) have not
942 * seen this pattern formalized anywhere and first arrived at it in a certain other (currently larger) project which
943 * depends on Flow. I then back-applied this pattern to Flow itself. While the presented convention looks laborious,
944 * we assure you over time it will save time and pain. It will help compile times and prevent circular-dependency
945 * awfulness that would otherwise plague larger APIs.
946 *
947 * Note this convention applies not merely to the API exported to the user; it applies, also, to internal impl
948 * code as it goes about its internal business (typically residing in detail/ sub-dirs).
949 *
950 * First the basic rules. Consider any declared symbol at the file level (i.e., direct
951 * member of a `namespace` -- not a data member): aggregate type (`struct`, `class`, `union`) or template thereof;
952 * `enum` or `enum class` (note: elsewhere we recommend avoiding the former) or template thereof; type alias
953 * (`typedef` or `using`, though note: elsewhere we recommend avoiding the former); variable or constant
954 * (non-`constexpr`); free function including `operator`s. (We exclude macros and `constexpr` "variables" for
955 * now; but see discussion below in separate section.) Note we are speaking only of symbols exported beyond a
956 * given translation unit; items local to a translation unit (anon-namespace or `static` items in a .cpp file, etc.)
957 * are not relevant here.
958 *
959 * - Aggregate types and templates thereof:
960 * - It must be forward-declared (e.g., `class X;`, `template<typename T> struct Y;`) exactly once.
961 * - It must be separately defined (meaning with its body provided), in compatible fashion, exactly once.
962 * - As shown elsewhere, this definition must be fully Doxygen-documented, including a leading doc header.
963 * - Enumerations and templates thereof:
964 * - It must be declared/defined exactly once, together with its full Doxygen docs (including leading
965 * doc header). Do not forward-declare. (Rationale omitted: But trust us; trying to fwd-declare it is
966 * more trouble than it is worth; and avoiding it generally well with other nearby conventions.)
967 * - Type aliases:
968 * - It must be declared/defined exactly once (as required by C++), together with its Doxygen doc header.
969 * - Varibles and constants (non-`constexpr`):
970 * - It must be forward-declared exactly once with `extern` keyword: `extern T var;`, `extern const T CONSTANT;`,
971 * together with its doc header.
972 * - It must be separately defined (initialized) exactly once: `T var; // Possibly initialized also.`,
973 * `const T CONSTANT = ...;`, `const T CONSTANT(...);`.
974 * - Free functions:
975 * - It must be forward-declared (a/k/a prototyped) exactly once, together with its Doxygen docs including
976 * doc header.
977 * - It must be separately defined (meaning with its body provided) exactly once in compatible fashion.
978 *
979 * For each type of symbol, there are (as you can see) either 1 forward-declaration and 1 definition, or just
980 * 1 declaration/definition. You already will know from elsewhere in this doc (and general C++
981 * knowledge) where to put most of these. Briefly:
982 * - aggregate type definition/body goes in some header .hpp;
983 * - (but what about fwd declaration?);
984 * - same for enumeration definition/body;
985 * - same for type alias definition;
986 * - variable/constant `extern` declaration goes in some header .hpp; initialization in some .cpp;
987 * - free function prototype (fwd declaration) goes in some header .hpp;
988 * - free function definition/body goes in .cpp except for templates, `constexpr`s (functions), and
989 * `inline`s (which we elsewhere discourage in favor of LTO) which go in .hpp.
990 *
991 * Without the _fwd.hpp pattern, the more detailed conventions are as follows:
992 * - When something goes into an .hpp, put it in whatever .hpp file seems right.
993 * - If something requires a declaration and a *separate* definition, and the above says that both belong in
994 * .hpp, then just put them both into the same .hpp (declaration up-top; definition down below in impl area).
995 * The Doxygen doc header should be on the declaration, not the definition, so the user sees it first.
996 * - And furthermore fwd declarations of compound types are simply not specified... throw them in when
997 * circular definitions arise, but generally, whatever; just wing it. However naturally the Doxygen docs
998 * shall be on the class/struct/etc. body itself and not any fwd-declaration of it.
999 *
1000 * However -- in this pattern that is *not* correct (at least not usually). Rather:
1001 *
1002 * **Key rule**: The forward-declaration, or only declaration+definition, shall usually be placed in a
1003 * *forward-header* file. A forward-header shall always be named with form X_fwd.hpp. This is not an *absolute*
1004 * rule, and exceptions to it are not all criminal; but they should be rare. The more exceptions there are, the more
1005 * pain is likely to occur down the line. We talk about good exception cases below. For now let's forge on:
1006 *
1007 * That rule, in and of itself, is pretty simple. Got a thing that must be declared/defined or forward-declared?
1008 * Put it in a _fwd.hpp. If there's only one definition/declaration, then that's that. If there's also a 2nd
1009 * definition/declaration (aggregate types/templates thereof; variables/constants; free functions/templates) then:
1010 * - Any code requiring use of a given symbol can include the relevant _fwd.hpp when only a forward-declaration
1011 * is required; e.g., if only referring to forward-declared type `T` by pointer or reference.
1012 * - If it is insufficient (e.g., when `sizeof(T)` must be known to compiler), the code can include the non-_fwd.hpp
1013 * header.
1014 *
1015 * This allows for (1) shorter build times; and (arguably more importantly) (2) the methodical pre-disentangling of
1016 * hairy circular-reference nightmares due to C++'s single-pass compilation model.
1017 *
1018 * The convention gets somewhat annoying when it comes to the details of *which* _fwd.hpp to place the
1019 * declaration. We note that different libraries do different things and not very consistently at that. We give
1020 * it our best shot to nail it down and remove ambiguity for Flow. Let's go: The _fwd.hpp files by convention are:
1021 * - In top-level module/namespace flow::X, flow/X/X_fwd.hpp, a/k/a/ *top-level forward-header*. Essentially this one
1022 * always exists, assuming flow::X exports any symbol.
1023 * - In some cases a top-level module may contain a sizable sub-module, in a sub-namespace; so namespace
1024 * flow::X::...::Y. Then there is potentially (not necessarily) flow/X/.../Y/Y_fwd.hpp, a/k/a
1025 * *sub-level forward header*.
1026 * - In a given detail/ sub-directory (direct or otherwise) of flow/X, so flow/X/.../Y/detail, there is potentially
1027 * (not necessarily) flow/X/.../Y/detail/Y_fwd.hpp. (Note: not detail_fwd.hpp or forward.hpp or ....)
1028 * This is known as *detail-level forward-header*.
1029 * - If the detail/ aspect of some module is particularly complex, there could be more sub-dirs under detail/;
1030 * technically it may be desirable to have a lower-level Y/detail/A/.../B/B_fwd.hpp. Hopefully you can reason
1031 * out what to do on your own based on the below discussion, but we won't formally spend time getting into it.
1032 * Use common sense/analogy thinking.
1033 * - At any dir level, for a given C.hpp focused on central aggregate type (or aggregate type template, or namespace,
1034 * or compact feature or ...) named ~C, there is potentially (not necessarily) C_fwd.hpp in the same dir.
1035 * This is known as *header-specific forward-header*.
1036 *
1037 * First decide which of the ~3 locations your declaration belongs in; then create that _fwd.hpp if needed; and
1038 * then put the declaration there in the appropriate section. So which one should it be? Answer: it shall be either
1039 * - in header-specific forward-header C_fwd.hpp; or
1040 * - in a top-level or sub-level forward header flow/X/X_fwd.hpp or flow/X/.../Y/Y_fwd.hpp; or
1041 * - in detail-level forward-header detail/Y_fwd.hpp (or possibly an even deeper-down one, but as noted we won't
1042 * get into this and leave it as exercise to reader).
1043 *
1044 * Place a symbol info C_fwd.hpp if (1) it has to do with (e.g., free functions are common) with type/feature/whatever
1045 * (often class) C; and (2) the *total* set of symbols to-do with C (free functions at least usually identified with
1046 * Doxygen @relatesalso command) is subjectively *larger* than the following boring/vanilla set:
1047 * ostream<< operator, swap(). In other words if class/struct C comes with a sizable free-function/etc. API operating
1048 * on C-ish things, then stylistically they all belong in a segregated C_fwd.hpp that shall accompany C.hpp.
1049 * (So then C_fwd.hpp will contain free functions, class/etc. fwd-declarations, variable/constant `extern`
1050 * declarations, enum declarations, alias definitions; C.hpp will contain class/etc. bodies/definitions.)
1051 * Otherwise -- the typical case being a mere boring ostream<<C operator -- put it in a non-C-specific catch-all
1052 * _fwd.hpp shared among C and typically other entities.
1053 *
1054 * Supposing you chose a non-header-specific _fwd.hpp (which is typically indeed the easiest choice, except for
1055 * Cs with large free-function APIs), then you just need to pick which _fwd.hpp applies. If your symbol
1056 * is under detail/, then use a detail-level forward-header; otherwise use a top-level or sub-level forward-header.
1057 *
1058 * In the latter case (which, again, is quite common), should it go into a top-level or sub-level forward-header?
1059 * Obviously if the symbol is directly in flow/X, then place it in the top-level X_fwd.hpp. If however it's in
1060 * flow/X/.../Y, then it's a subjective decision.
1061 * - Simplicity is best; so if it does not seem particularly offensive, just don't create sub-level forward-headers;
1062 * just use the catch-all one at the top.
1063 * - However, if truly a sub-module of flow/X is its own (large) animal that is intentionally segregated from
1064 * the rest of flow/X, then you'll want to bite the bullet and create its own flow/X/.../Y/Y_fwd.hpp.
1065 *
1066 * Note on doc headers, in case it doesn't flow naturally from the above:
1067 * - For the guys where there is only declaration/definition, obviously the doc headers shall be on that one.
1068 * - For the guys where there is a declaration/prototype + separate definition/body:
1069 * - Aggregate types: doc headers go above and throughout the definition/body (hence .hpp).
1070 * - Constants/variables: doc header goes above the `extern` declaration (hence _fwd.hpp).
1071 * - Free functions: doc header goes above the prototype (hence _fwd.hpp).
1072 *
1073 * I (ygoldfel) am sad at how many words I have written; and worry it might be hard to understand. Nevertheless
1074 * I had to try to write it, if only to clarify my own thoughts on the matter for myself. Hopefully it helps.
1075 *
1076 * That said, the most important thing is to be disciplined in placing the forward (or only) declaration in a _fwd.hpp
1077 * file. It really does save pain as a project grows. */
1078
1079/* - Corollary: In more complex modules _fwd.hpp files will at times need to `include` other _fwd.hpp files, whether
1080 * from the same module or others. This should be straightforward to reason about; you'll see. One corollary
1081 * is that, if one follows the above rules, a _fwd.hpp will ~never contain struct/class/union or free-function bodies,
1082 * only their forward-declarations and prototypes respectively. A corollary of *that* is that if _fwd.hpp includes
1083 * another (in-project) header, then it should ~always itself be _fwd.hpp. Otherwise you've broken the point of
1084 * the outer _fwd.hpp's existence which is to *only* forward-declare things. Otherwise compilation times may
1085 * increase, and tricky circular issues may arise. */
1086
1087// -- STYLE GUIDE: Exceptions to _fwd.hpp pattern --
1088
1089/* As noted above: Above are not *absolute* rules, and exceptions to it are not all criminal; but they should be rare.
1090 * The more exceptions there are, the more pain is likely to occur down the line.
1091 *
1092 * Here are known reasonable exceptions as of this writing.
1093 * - Usually a _fwd.hpp should not include a struct/class/union (or template thereof) body... but sometimes
1094 * it is okay to do so. Hand-wavily speaking this is the case when the type C is a *glorified alias* or
1095 * *empty*.
1096 * - Empty types, usually `struct C {}`, are used in the tag-parameter pattern among other things.
1097 * They're fine to include directly in _fwd.hpp. Omit the forward-declaration; declare directly in _fwd.hpp;
1098 * include the doc header as you would normally in non-_fwd.hpp.
1099 * - Similarly for types (for example flow::Container_traits) that lack any data but have only `static` constants
1100 * and/or type aliases.
1101 * - Consider a class/class template that non-virtually subclasses another class/class template but adds no
1102 * data members outside of `static` constants -- so only a ctor/method API and/or static constants. If this
1103 * added API + constants is small enough -- e.g., adding convenience constructor(s)
1104 * and/or a couple compatibility accessors -- then it can go into _fwd.hpp. Omit the forward-declaration;
1105 * declare directly in _fwd.hpp; place the doc header as you would normally in non-_fwd.hpp. This class
1106 * is "almost" an alias to its superclass.
1107 * - If the class/class template C is too large to stylistically belong in a module-level _fwd.hpp, you might
1108 * place it into its own C.hpp; but it is then okay to `include` in that _fwd.hpp. If C is oft-used, this
1109 * might be convenient for the user.
1110 * - (For example Flow defines flow::Function<> which subclasses std::function<> and adds some minor convenience
1111 * APIs but no data.)
1112 * - Similarly consider a struct or class that merely wraps a single item, for type safety and/or to provide
1113 * a little wrapper API. For example: a `struct Native_handle { int m_fd; }` with a few niceties like
1114 * an ostream<< operator and perhaps a couple convenience methods. It is ~always copied by value, like an int.
1115 * So it's "almost" an alias to the int. So it can go into _fwd.hpp. Again: Omit the forward-declaration;
1116 * declare directly in _fwd.hpp; include the doc header as you would normally in non-_fwd.hpp.
1117 * - Again you might instead put it into its own X.hpp... but then `include ".../X.hpp"` in module-level
1118 * _fwd.hpp.
1119 * - Top-level flow/common.hpp is something of a special case: It lives outside of (above) any top-level module;
1120 * and it *is* a forward-header... but not named that way. Still, that is what it is. Anyway... it's a special
1121 * case, and as noted in that file things should be added to it *very* sparingly in any case. What if
1122 * we want something "heavy" (a class body, for example) in common.hpp? Doesn't that break the convention of
1123 * not doing that in forward-headers? Answer: yes, it hypothetically would, so you shouldn't do it... but
1124 * as noted common.hpp is intentionally *very* limited/small. If something is that big then it should go into
1125 * flow::util or some other module, not directly into namespace `flow` and thus common.hpp.
1126 * - Incidentally it happens to feature the aforementioned flow::Function<>, essentially or "almost" an alias
1127 * to std::function. */
1128
1129// -- STYLE GUIDE: _fwd.hpp pattern odds/ends: Macros, constexpr globals --
1130
1131/* Where does a (functional) macro go? _fwd.hpp or not? If the former then which one? For now we leave the answer
1132 * mostly as an exercise to the reader. We'll only say this:
1133 * - Macros (as noted elsewhere) should be avoided whenever possible (e.g., flow::log uses them, as there's ~no
1134 * way to avoid it given the perf needs involved). If you do need to export a macro, then remember *what* a macro
1135 * is: The #define itself does not "do" anything: something only executes when this guy is actually invoked in
1136 * user code. The #define does not affect compile times really, nor do circular dependencies really apply to it.
1137 * - One rule of thumb might be:
1138 * - Macro goes in _fwd.hpp if and only if *every* symbol it needs -- directly or indirectly -- can be and is
1139 * (directly or indirectly) `include`d by the same _fwd.hpp (which means each lives in this or other _fwd.hpp),
1140 * enough so to compile without additional `include`s by the user.
1141 * - Otherwise it goes in non-_fwd.hpp (which must also, directly or indirectly, supply all necessarily
1142 * symbols by `include`ing the proper headers to avoid compile or link error). /
1143
1144/* What about a constexpr global (a constant)? For now we skip this topic. The current suggestion is to not use
1145 * such constexpr globals; `static constexpr` class/struct members should be sufficient, if indeed constexpr is
1146 * required. If this changes we'll develop a convention in this spot. It might rely on the C++17 `inline constexpr`
1147 * syntax perhaps. TODO: Cross that bridge when we come to it. */
1148
1149// -- BEST PRACTICES --
1150
1151/* The rest of the doc lists various best practices, which we've already explained are guidelines that subjectively
1152 * help readability, maintainability, and elegance. To be clear:
1153 * - These are no more or less mandatory than the cosmetic STYLE GUIDE rules above. However, due to their subjective
1154 * nature, their correctness is much more arguable; and (as a corollary to that) good reasons to bend or break
1155 * a "best practice" tend to be much more frequent than for the cosmetic guidelines.
1156 * - The cosmetic rules come close to being exhaustive, meaning you're free to do whatever you want, as long as
1157 * you don't break the existing cosmetic conventions. The BEST PRACTICES, however, are nowhere near exhaustive!
1158 * Good style is something picked up over years, even decades. Entire famous books are written about it
1159 * (e.g., "Effective C++" by Meyer). Some patterns are hard to even explain due to various details and caveats.
1160 * - *Therefore*, the following isn't even a little exhaustive: more like a list of things that:
1161 * - have come to mind; and
1162 * - are fairly uncontroversial; and
1163 * - can be described in a few paragraphs at most -- ideally only 1 -- including the [rationale] if needed; and
1164 * - are sufficiently high-impact to deserve the real estate in this doc.
1165 *
1166 * Note: Add more of these, as they come up. Anyone can add them but must pass code review, like regular code.
1167 * Like, one can't just decide for everyone that multiple inheritance isn't allowed -- without review.
1168 * Ideas that feel controversial should be kicked up to a wider team as well. */
1169
1170// -- BEST PRACTICES: Comments --
1171
1172/* - Explain anything *possibly* unclear to a solid-but-unfamiliar coder, via comments written in plain English. -
1173 *
1174 * If something could *possibly* be unclear to a solid coder *unfamiliar* with the code base or specific subject matter,
1175 * just explain it with a comment! Use plain English, not cryptic scribblings, even if it's less pithy.
1176 *
1177 * JUST. EXPLAIN. THINGS. IN ENGLISH. UNLESS ABSOLUTELY OBVIOUS.
1178 *
1179 * If you're unsure whether something is obvious or not, just assume not. Worst-case, it'll be redundant. So?
1180 * It's worth it.
1181 *
1182 * Whether it's laziness or the inexplicable feeling that long comments are just not "cool," most coders are very
1183 * lacking in this area by default. Let's not be that way. */
1184
1185/* - Doc header (comment) of *any* function *must* describe the black-box contract of the function. -
1186 *
1187 * It *must* describe fully how to *use* f() as black box, without
1188 * requiring one to look at its code (if that were even possible, which it often isn't). This black-box description
1189 * must not include implementation details. This is a/k/a the function's *contract*, and it is holy.
1190 * - You may use the language of "pre-conditions" and "post-conditions." It's not mandatory but can be helpful for
1191 * clarity for more complex `f()`s.
1192 *
1193 * The f() comment *may* discuss implementation details in additional, clearly marked 1+ paragraphs.
1194 * - Typically implementation is best-discussed inside the { body }, so putting it in doc header is fairly rare.
1195 * (*Sometimes* user's "how to use" insight is increased by knowing some key aspect of implementation.)
1196 *
1197 * This rule can be regularly bent in only 1 instance: A `private` (or conceptually equivalent, like `static` free)
1198 * f() can be documented in a more white-box fashion when a formal black-box contract would feel silly. Basically,
1199 * little obscure helpers can bend the rule within reason. At your discretion, say something like "Helper of [...]'
1200 * that [...does basically so-and-so...]; see its code for further info. " However, @param, @tparam, and @return must
1201 * all be formally commented even so.
1202 *
1203 * [Rationale: The benefits are:
1204 * - The obvious: Reader/maintainer can quickly understand f() without looking inside f() {} (which might even be
1205 * unavailable).
1206 * - The subtle but important: It helps avoid spaghetti code! Essentially, f() is an abstract component that
1207 * performs a black-box task. By forcing coder to explicitly describe it *as* a black box, it tends to discourage
1208 * abstraction-breaking designs that require the caller to worry about the callee's innards. It's
1209 * a powerful anti-spaghetti technique in our long experience.] */
1210
1211/* - Every class (and other compound types) *must* have a user-guide-like doc header with a black-box contract. -
1212 *
1213 * In spirit it's similar to the guideline above re. function doc headers. In practice the class doc header requires
1214 * more effort however. Rule of thumb for what to include in a class/struct/etc. doc header (in order):
1215 * - The first pre-period sentence ("brief" in Doxygen parlance): Describe what an object of this type represents
1216 * and any major capabilities, within reason.
1217 * - The following paragraph (or the rest of the 1st one) may expand on that, featuring the most essential black-box
1218 * information.
1219 * - Next, explain *how to use* the class/whatever (a/k/a an object's lifecycle). Like: First, you construct it with
1220 * purpose/details X. Then, you use so-and-so methods; then after that you may use these other methods. Destroy
1221 * the object when Y. Spend multiple paragraphs if helpful for clarity. Pithy is good but clear is better!
1222 * - All the members (including functions) must be documented with doc headers; so no need to get into that except
1223 * for clarity. Worst-case, they'll go and look at the method/whatever, especially if you mention the thing
1224 * as noteworthy.
1225 * - (Optional, often not needed) Discuss implementation strategy. This must be clearly marked, same as with f()
1226 * doc headers. However, unlike with f(), it is often *better* to discuss implementation strategy in the doc
1227 * header, as there's no good other place (unlike with functions, where f(){ } comments are a fine place for it).
1228 * - If the class/struct/whatever is part of the public API, place `@internal` before this discussion, so that
1229 * the public API generated docs omit these blatantly white-box (internal) explanations.
1230 * - A list of any `@todo`s. Black-box/feature to-dos typically go at the end of black-box write-up;
1231 * Internal/white-box to-dos at the end of the optional implementation write-up. Each @todo is like one of these:
1232 * - @todo One pre-period sentence sufficiently describing a simple to-do.
1233 * - @todo One pre-colon sentence summarizing the complex to-do: Then get into as much detail as desired within
1234 * reason, to make the to-do easier to "do" for the person reading it much later. Keep going and going but
1235 * absolutely no more than one paragraph. Indentation should be as seen here.
1236 * - Note: Following 1 of those 2 formats will produce a nice-looking generated TO-DO page via Doxygen.
1237 * - Note: `@todo`s on functions and other members are fine too. Class/etc. doc headers are just a natural place
1238 * for the bulk of them.
1239 * - Similarly a list of any `@note` and/or `@see` paragraphs. Indent them similarly to @param. There's no
1240 * special page generated by Doxygen for these, so this is purely for readability pleasantness and could instead
1241 * be directly in regular doc header text. Whatever seems clearest is fine.
1242 *
1243 * All but the first 1-2 bullet points can often be omitted for simple compound types such as data-store `struct`s.
1244 *
1245 * [Rationale: In our experience, few coders do this by default. Class doc headers tend to be non-existent or very
1246 * lacking. Yet also in our experience these tend to be the most useful comments and hence most painful when omitted.
1247 * Classes are central entities, so these comments (when good) tend to help the most in explaining how stuff works
1248 * at a high level (a/k/a the architecture). If I don't broadly know what the class is *for*, I will tend to write
1249 * ad-hoc (spaghetti) code due to lacking this conceptual understanding and instead understanding it "heuristically,"
1250 * by observation of how it's already used. And if those use sites aren't well commented... then....] */
1251
1252// - If something is already *clearly* expressed with code, don't add a redundant *in-line* comment saying it again. -
1253#if 0 // NO. Not helpful.
1254if (mutex.locked()) // Mutex being locked here means we are in trouble.
1255{
1256 throw_error();
1257}
1258#endif
1259
1260// -- BEST PRACTICES: Inlining --
1261
1262/* - Disregard the following rule when writing *very* perf-sensitive code written *specifically for perf reasons*.
1263 *
1264 * We shall not try to be ultra-precise about this. Use your judgment. There are not frequent situations where
1265 * explicit inlining is required for perf in practice; even less frequently would "let LTO handle it" not cover it.
1266 * They do exist though. If, say, you're writing widely used code that's to be specifically faster than some
1267 * STL or Boost utility that's already speedy -- and it is so quick that the call overhead is a major part of it --
1268 * `inline` away. */
1269
1270/* - Do not use explicit or implicit inlining; let compiler/linker handle it. -
1271 *
1272 * This requires some discussion. As you may well know, there are two ways to make a function or method inlined
1273 * in the generated code:
1274 * - Declare the thing `inline` in X.hpp, then later in same X.hpp provide the { body }.
1275 * - For class/struct methods only, place the { body } directly at the method's declaration inside the
1276 * class/struct { body }. (It's commonly used for accessors; also for example code snippets.)
1277 *
1278 * Either way, this will (in most cases, excluding recursion and such) force the compiler to inline the function.
1279 * The rule here is:
1280 * - Simply don't do that ever.
1281 * - Instead, we will use `gcc -O3` or equivalent, so that the compiler/linker will decide for us when things
1282 * should be inlined.
1283 *
1284 * [Rationale: The positives are as follows. Firstly, compilation is much faster in many situations: changing
1285 * { body } in X.cpp instead of in X.hpp means only X.cpp must be recompiled; otherwise potentially *everything*
1286 * must be recompiled. This can speed up day-to-day work by a ton; much less waiting for compilation to complete.
1287 * Secondly, it's better stylistically: declarations go in headers; implementations go in .cpp; and this is always
1288 * consistent, except for templates where "c'est la vie." Thirdly, it removes the onus from the developer in terms of
1289 * deciding what is worth inlining and what isn't; the compiler can deal with it.
1290 *
1291 * There *is* a significant negative however. The compiler is great at auto-inlining things within a given
1292 * translation unit (.cpp->.o, basically). However if X.cpp calls something inlined in Y.cpp, then X.cpp invoking
1293 * that function cannot actually inline; since separates compilations are separate, the compiler command to
1294 * compile X.cpp has no choice but to call the real, non-inlined function (Y.cpp's own invocations of that function
1295 * are still inlined). Fortunately they thought of this (eventually): link-time optimization a/k/a LTO or FLTO.
1296 * Essentially in this mode the linker works together with the compiler and removes this negative.
1297 * Unfortunately the support in compilers for this is somewhat uneven. In my (ygoldfel) experience, clang fully
1298 * implements this out of the box these days, and I've been using it on Mac delightfully; however in Linux I've found
1299 * more work -- like swapping out the linker for a fancy clang-associated one -- was necessary and, while achievable,
1300 * I personally never spent the required time to fully achieve it. gcc supports the feature, but it stands to reason
1301 * that to turn it on successfully is a multi-day project, and the newer the gcc version the easier it will probably
1302 * be.
1303 *
1304 * Given the last paragraph, why are we still placing the rule here? Answer: It's a trade-off. The positives are
1305 * clear and obvious; while the negative's actual perf impact is theoretically there but in practice questionable.
1306 * So we've made the call to go with known positives and to deal with the negative if or when necessary in practive.
1307 * Since LTO exists in the compilers we use, we know the path to take when/if needed.
1308 *
1309 * Update: Now that Flow is an open-source project with a nice CI/CD pipeline, we auto-test with a number of modern
1310 * clang and gcc versions; all of them support LTO; and this support works just fine. Hence the above objection
1311 * to the tune of "uneven" compiler support is no longer accurate; the support is quite good and even.
1312 *
1313 * To summarize, some may see this as a controversial rule. We can iterate on this if someone feels strongly the
1314 * above logic does not stand up to scrutiny. In terms of my (ygoldfel) experiences, which span now almost a decade,
1315 * the lack of full-on inlining has not been a practical perf issue. It is even quite possible that the gains from
1316 * letting the compiler decide (`gcc -O3`) instead of the human constantly having to make the judgment call about what
1317 * to inline (`gcc -O2` + explicit inlining by dev) exceed the losses from LTO not being in effect yet. Update:
1318 * with widespread LTO support in all modern gcc and clang compilers this complaint is mitigated/eliminated.] */
1319
1320// -- BEST PRACTICES: Constructor and initializer invocation --
1321
1322/* Before C++11, the language had an annoying ambiguity, inherited to this day; but if you follow the recommended
1323 * conventions, you will bypass the ambiguity. So what's this ambiguity? It is, basically, the following.
1324 * Note we're not trying to be complete and ultra-correct here; just giving a sense, for background: */
1325T a(); // Is this constructing an object named `a` of type T, via its default constructor T::T()?
1326T a(); // Or maybe it is a function named `a` that returns a `T`?
1327
1328/* So the modern Flow style is to entirely avoid the `T()` or `T a()` style of invoking a constructor.
1329 * Use the brace{} form instead.
1330 *
1331 * Some old-style ctor-invoking code may be encountered, as Flow originated before C++11. Ideally fix it on sight.
1332 *
1333 * There are more subleties, benefits, effects beyond removing ambiguity and avoiding strange compiler errors, but
1334 * we aren't trying to copy/paste Effective C++ here. Please just trust us and do it. */
1335T a; // No args, no problem. Could also write `T a{}`; why bother though?
1336f(T{}); // No args and no name. Use braces to explicitly convey it's a ctor invocation; not: `f(T())`.
1337T a{1}; // Takes an arg... similarly use braces.
1338auto x = string{"abc"}; // Not: auto x = string("abc").
1339f(string{"abc"}); // Not: f(string("abc")).
1340
1341// If you're using an initializer-list and/or direct-init, please indicate this by placing spaces after { and before }.
1342struct Complex { float m_real; float m_imag; };
1343Complex num{ -2, 2.3 }; // Initializing via direct-init, not via a constructor: indicate with spaces.
1344f(Complex{ -2, 2.3 });
1345
1346/* Do be careful of one thing: a container with an initializer_list ctor will invoke it over the would-be constructor
1347 * with a matching signature (if any). Unfortunately one must revert to the parentheses-using-constructor-call style.
1348 * (Needless to say, the compiler doesn't care if you put spaces in there; that is for humans only.) */
1349vector<int> v{ 10, 1 }; // 2 elements: 10, 1. initializer_list ctor is invoked, even though the following ctor matches.
1350vector<int> v(10, 1); // 10 elements, each one equal to `1`. Force compiler to ignore the initializer_list ctor.
1351// Nevertheless, prefer {} for construction whenever possibe which is most of the time by far.
1352
1353// When the type is built-in like the integers and real numbers, construction via (), {}, and static_cast<> are all OK.
1354f(size_t{-1}); // Might not compile, because size_t is unsigned, and this conversion is from signed -1.
1355f(size_t(-1)); // OK: defeats the compile error/warning; stylistically no big deal, as size_t is basically built-in.
1356f(static_cast<size_t>(-1)); // OK: explicit cast. It's a bit wordy though, so the preceding could be preferred.
1357// Corollary w/r/t multi-word type names:
1358f(unsigned int{-1}); // SYNTAX ERROR. This is due to the space in the type name.
1359f((unsigned int)-1); // BAD. By convention we do not use draconian C-style casts, even though it works.
1360f(static_cast<unsigned int>(-1)); // Defeat the syntax error. Could also `using uint = unsigned int` or something.
1361
1362// -- BEST PRACTICES: Naming vis-a-vis units, time units, time math --
1363
1364// - Do: Include units in names that represent quantities, at each name's end, in singular form; except bytes.
1365size_t buf_sz; // Bytes: no units.
1366float metric_velocity_kph; // Other quantities with units: include units.
1367
1368// - Do: For bytes, do still use (when applicable), the multiplier prefixes: ki (1024), mi (1024^2), etc. -
1369// - Do: Use ki/mi/... when the multiplier is a power of 1,024; k/m/etc. when it is a power of 1,000. -
1370const size_t RAM_LIMIT_MI; // Mebibytes: just show the mega part.
1371const uint64_t HARD_DRIVE_CAPACITY_G; // Gigabytes (billions of bytes): just show the giga part.
1372
1373// - Do: Use `u` for micro. - Do not: use `micro` or the greek non-ASCII symbol or `m`. -
1374// - Do: Use `sec` (not `s`) for seconds. It is not metric, but that's a chosen convention.
1375const auto count_usec = boost::chrono::microseconds{some_dur}.count();
1376
1377// - Do not: Use a naked numeric type for durations whenever `chrono` is usable. -
1378/* - Do: Use `chrono` for all durations and time points. Prefer `boost::chrono` to `std::chrono`. -
1379 *
1380 * Math using `chrono` is type-safe (precision cannot be lost without an explicit `duration_cast` or `round`
1381 * or `ceil`) and feature-rich; yet exactly as fast and memory-compact as manual time operations.
1382 * There is no rational argument to not use it whenever possible.
1383 *
1384 * When a raw tick count is required, such as by an 3rd-party API, use `duration::count()`.
1385 *
1386 * `std::chrono` and `boost::chrono` are nearly identical. However, as of this writing and the versions involved,
1387 * the latter is (1) ubuquitous in Flow and (2) has several *very* useful additional features.
1388 * - I/O: Stream (e.g., cout) output straightforwardly <<s durations and time points along with units.
1389 * (Input works similarly.) This is not yet available as of C++17 std::chrono.
1390 * - Clocks: `boost::chrono` has some additional clocks types such as a thread-time clock. */
1391using boost::chrono::microseconds;
1392using boost::chrono::milliseconds;
1393using boost::chrono::seconds;
1394using boost::chrono::round;
1395auto period = microseconds{332} + seconds{2}; // Automatic conversion math.
1396auto period_msec = round<milliseconds>(period).count(); // round()/etc. required: usec -> msec would lose precision.
1397auto period_msec = milliseconds{period}.count(); // NO: Will not compile.
1398old_api_that_takes_msec(period_msec);
1399
1400// - Do not: Specify units on `chrono` duration values. A duration is a duration; that's `chrono`'s thing. -
1401Fine_duration some_period; // It's nanoseconds but so what?
1402microseconds some_period; // Ditto.
1403/* - Do: Specify units on `chrono` duration values if the resolution carried is specifically important. -
1404 *
1405 * For example a config value that can only be given with the coarseness of seconds should still be a
1406 * `chrono::duration`, for conversion safety and other goodies; but it's important to specify that whatever value
1407 * one gives cannot be more precise than seconds. */
1408Fine_duration cfg_period; // It's a duration. Any units are fine.
1409Fine_duration cfg_period_sec; // It's a duration, and the stored count is of seconds, nothing better.
1410
1411/* - Do: Prefer simpler/less-templated APIs that take `chrono` values as `Fine_duration`s and `Fine_time_pt`s. -
1412 *
1413 * Very general APIs, when taking `chrono` quantities, *sometimes* tend to be templates that allow the
1414 * use of any unit-type. flow::Fine_duration is `nanoseconds` and represents the highest precision and bit-width
1415 * generally appropriate in practice, yet reasonable to hold durations of ~any coarseness. (flow::Fine_time_pt
1416 * is the corresponding time-point type.) Using these tends to make for simpler APIs while losing ~no flexibility
1417 * or syntactic sugar. `Fine_clock::now()` yields `Fine_time_pt`s.
1418 *
1419 * It's not a hard rule. If it is a public-facing API in a very general utility, you may feel
1420 * `template<typename Rep, typename Period> f(chrono::duration<Rep, Period> period)` or even just
1421 * `template<typename Dur> f(Dur period)` is preferable. It is your call. Do note that, e.g., boost.asio
1422 * in essence goes the `Fine_duration` route. */
1423void schedule_at(Fine_time_pt when, ...);
1424void schedule_from_now(Fine_duration period, ...);
1425
1426// - Do: Follow *suggested* naming conventions for duration and time-point names. Reduces ambiguity; ups consistency. -
1427Fine_time_pt m_stopped_when; // Time stamp: `when`, `WHEN`. (Not `timestamp`, `time`, etc.)
1428Fine_duration m_log_period; // Duration: `period`, `PERIOD`. (Not `duration, `time`, etc.)
1429void schedule_at(Fine_time_pt when); // Action to be performed at a specific time: `_at`.
1430// Action to be performed after a delay from <current time>: `_from_now`.
1431void schedule_from_now(Fine_duration period_from_now);
1432
1433// -- BEST PRACTICES: Doxygen doc header deep-dive --
1434
1435/* In "BEST PRACTICES: Comments" above we mentioned doc headers. Let's get into the topic more.
1436 *
1437 * Every class/`struct`/union, function, macro, constant, variable, namespace,
1438 * file, exception, function parameter, template parameter, macro parameter, and anything else I forgot to list
1439 * MUST be accompanied by a "Doxygen comment," possibly shared with one or more other items (e.g., a single
1440 * Doxygen comment will cover the *function* itself, each of its *parameters* and *template parameters*, and its
1441 * *return* value). Run Doxygen as covered in the aforementioned README; you've documented what you needed to
1442 * if any only if Doxygen does not complain, and the doc generation script finishes successfully (exit code 0).
1443 * A "Doxygen comment" is, to be clear, simply a comment with an extra one or two characters to run Doxygen's
1444 * attention to it: `/ * *` (sans spaces) or `///` and (usually but not always) containing Doxygen "commands"
1445 * starting with the `"@"` character: `"@param"` or `"@return"`.
1446 *
1447 * Do not Doxygen-document things just to say they don't exist, when that's already obvious. Most notably, no
1448 * `"@return"` for `void` functions/methods and `void`-like functional macros. There are probably others but just
1449 * follow the principle.
1450 *
1451 * Do not copy/paste documentation, unless some other constraint forces you to do so. Instead, just refer
1452 * to the other documentation bit in all the instances except the 1 being referred to by others.
1453 * (This has its own maintenance problems, but we believe they are far outweighed by the
1454 * maintenance headache of copy/pasting explanatory sentences and paragraphs.)
1455 * This applies to all documentation, including inline comments inside function/method bodies. Sadly, the
1456 * constraint that every "item" must have a Doxygen comment will often force you to copy/paste. Even so,
1457 * do your best. Specifically, if 4 of 5 parameters of function `f2()` use identical semantics to 4 of 7 parameters of
1458 * function `f1()` then -- while you ARE forced (by the "every" rule above) to document 4 of the 5 `f2()` parameters --
1459 * the body of the doc of each of the shared parameters can simply say "`See f1().`" instead of copy/pasting
1460 * its actual semantics. This is the most prominent copy/paste situation, but the same principles can be applied
1461 * to others that may pop up.
1462 *
1463 * Do not use `"@brief"` explicily; use the first-sentence rule instead. It's already clear by example
1464 * that we do not use `"@brief"`, but the rule that IS used is non-obvious from just looking at the code: Namely:
1465 * The first *sentence* of the Doxygen comment of the function/method/macro or
1466 * class/`struct`/union is taken as the brief description of the item. A "sentence" is defined as everything up
1467 * to and including the first period (.). (Watch out for "a.k.a." and such: use "a/k/a" instead.)
1468 * Everything after that first sentence is the detailed description and is separated from the brief one on the
1469 * page (and, in many places, the brief description is present along with item name, while detailed one is
1470 * not there at all; for example the Class List page). (After the description are other sub-items such as
1471 * `"@param"` or `"@see"` or `"@todo"`.)
1472 *
1473 * ### Doxygen markup within a Doxygen command ###
1474 * This section originally existed in the class net_flow::Node doc header, and I have left it there, because that is
1475 * a large class which generates a Doxygen doc page (unlike the present comment) with many examples available for
1476 * clarity. Please see that doc header's identically titled subsection ("Doxygen markup," etc.).
1477 *
1478 * @warning Please read it! Also, if you intend to add to/otherwise change Doxygen-use conventions in the Flow project,
1479 * please make sure you document it in that section as opposed to simply doing it and creating undocumented
1480 * inconsistency (especially over time). */
1481
1482// -- BEST PRACTICES: Namespaces, libraries --
1483
1484// - Do: Use `namespace` to wrap *every* symbol in a namespace, whether public or internal. -
1485
1486/* - Corollary: Since #define macros cannot belong to any namespace, "simulate" it with the `NAME_SPACE_` convention. -
1487 *
1488 * namespace outer { namespace inner { [...stuff...] } }
1489 * #define OUTER_INNER_[...stuff...]
1490 *
1491 * Example: A class about logging in Flow: `namespace flow { namespace log { class Logger; } }
1492 * A macro about logging in Flow: #define FLOW_LOG_WARNING([...]) [...] */
1493
1494// - Do: Liberally use `using a::b::thing;` inside function { bodies }. -
1495// - Do: Use `namespace convenient_alias = a::b;` inside function { bodies }. -
1496// - Corollary: Avoid: heavy explicit namespace-qualifying in function { bodies }; prefer `using` 90%+ of the time -
1497
1498// - Do not: Use file-level `using` or `using namespace`; especially never ever in headers! -
1499
1500/* - Do: Use `boost::` (Boost) or `std::` (STL) over other libraries or self-written facilities when possible -
1501 *
1502 * STL facilities by definition have a high expectation of reliability and performance, though the latter should be
1503 * verified for each feature when performance is paramount. Such code is also much more portable and maintainable
1504 * than many of the alternatives. Also, cppreference.com documentation is *fantastic*.
1505 *
1506 * Most STL facilities originate in Boost and then gain entry to the C++ standard and actual STL.
1507 * Hence Boost is considered to have equivalently high expectations of reliability, perf, *and* documentation.
1508 * Moreover, Boost has tons of features (in particular entire modules) not (yet) in STL. */
1509
1510/* - Corollary: Slight preference to boost:: over std:: when a given feature is equivalently in both.
1511 *
1512 * It's very common that std::...::X has boost::...::X counterpart. In this case they're either exactly equal, or
1513 * the one in boost:: is a ~100% compatible super-set (equal, *plus* certain added APIs). Either facility can be used,
1514 * unless you need one of the added features (e.g., boost::chrono:: equals std::chrono:: but has a thread-time clock
1515 * class added, among others).
1516 *
1517 * When you can use either facility, Flow leans to Boost. A vague exception are C++03 interfaces
1518 * like std::string, std::vector, std::map.
1519 *
1520 * [Rationale: Honestly the latter "lean" is mostly a historical thing: In 2012 we were limited to C++03, so STL
1521 * was *much* more limited. Since then there has been no reason to switch to std::, and several instances where
1522 * Boost-only features proved useful in their availability, we've conservatively stayed with this principle.
1523 * Possibly if Flow were developed in 2019, we might have started off differently and would lean to std::.
1524 * It wasn't, so for now it is prudent to stay the course (don't fix it if it ain't broken).] */
1525
1526/* - Corollary: Use, e.g., `using Thread = <std or boost>::thread` for commonly used things from STL/Boost features. -
1527 *
1528 * [Rationale: 2 benefits. One, if it's commonly used then this increases pithiness and cosmetic consistency.
1529 * Two, particularly for items in both STL and Boost, it makes it easy to switch the underlying implementation.] */
1530
1531/* - Do: In particular, familiarize self with these boost.* modules: -
1532 *
1533 * Basic tools (should be familiar at minimum):
1534 * any, array, chrono^, core, dynamic_bitset, lexical_cast, random*, unordered#,
1535 * smart_ptr (+movelib::unique_ptr), system^, thread^&, tuple, unordered, utility
1536 * More advanced but similarly uncontroversial:
1537 * atomic$, date_time, filesystem, heap, ratio, stacktrace, timer, tribool
1538 * Advanced and should be decided on with care; but institutionally known to be solid:
1539 * asio%, format, interprocess, intrusive, io, iostreams, program_options, regex, algorithms, string_algo@
1540 * No personal or institutional experience; advanced; should be looked into opportunistically:
1541 * context, couroutine2, fiber - Relates to "micro-thread" concept.
1542 * exception - Also see guidelines re. exceptions elsewhere in this doc.
1543 * beast - Write HTTP-WebSocket-... servers, on top of boost.asio.
1544 * pool
1545 * log - Flow has its own flow::log framework. A top-level doc header discusses why using that and not boost.log.
1546 * Do not use:
1547 * function!
1548 *
1549 * -%- First see flow::async::..., which provides the most convenient way to work in the boost.asio style.
1550 * -*- See also Flow's random.hpp.
1551 * -#- See also flow::util::Linked_hash_{map|set}.
1552 * -^- Check out the flow:: public `using` aliases for stuff inside these modules.
1553 * -&- Also: There's a long-standing ticket about the slowness of boost::shared_mutex (sans contention) even as of
1554 * Boost-1.80. It might be better to use std::shared_mutex. However consider that experts say a regular
1555 * mutex is blindingly fast to lock/unlock, and thus any `shared_mutex` is only "worth it" with long read
1556 * critical sections and their very frequent invocation; this is apparently quite rare.
1557 * -@- Be very careful about performance. Lib is highly generic and might be too slow in some contexts (<-experience).
1558 * -$- Performance-sensitive; perf bake-off vs. std:: might be relevant in some cases.
1559 * Update: Current (8/2023) wisdom appears to be to possibly prefer std::atomic to boost::atomic; suspecting
1560 * better or at least not-worse perf due to native nature of it. It can go either way; but current preference
1561 * in Flow has switched to std::atomic. (A perf bake-off might be nice, but std::atomic really should be safe
1562 * and full-featured.)
1563 * -!- boost.function is, it turns out, horrendously slow (too much capture copying). Plus it has a 10-arg limit.
1564 * std::function in gcc looks great in both regards though lacking some very minor APIs from boost.function.
1565 * Just use flow::Function: it's the best of both worlds. Definitely do not use boost.function! */
1566
1567// -- BEST PRACTICES: Error handling --
1568
1569// - Do: Use assert() for bad arg values as much as possible *unless* returning an error is worth it to user. -
1570// - Do: Use static_assert() for compile-time checks, or after failing an #if[def] (or other) compile-time check. -
1571/* - Do not: Use `#error "<message>"` after failing an #if[def] (or other) compile-time check. -
1572 *
1573 * `static_assert(false, "<message>")` is quite a bit more convenient. */
1574// - Do: Use `if constexpr()` for compile-time checks. -
1575/* - Avoid: using SFINAE (look it up please) -- when possible. -
1576 *
1577 * SFINAE is tough to comprehend and code. `if constexpr()` often makes it unnecessary and is much easier
1578 * to follow and code. */
1579
1580/* - Do: Use Flow's standard error reporting convention/boiler-plate when returning errors from a public lib API. -
1581 *
1582 * This allows for returning both error codes (boost.system ones) for high perf *and* optionally wrapping that in
1583 * an exception if so desired. Read more starting in flow::Error_error code doc header. See also some notes on error
1584 * reporting in common.hpp. */
1585
1586/* - Do: ...follow the following thoughts when considering using exceptions or not. -
1587 *
1588 * Question 1 is whether to optionally throw an exception on error at the *top* level of a given *public* *libary* API.
1589 * That's covered in the previous guideline. Here we talk about whether to throw exceptions *throughout* the
1590 * implementation, which is a different question. So, suppose an error has been detected. Then:
1591 *
1592 * Basic fact: Fastest is to assert(). Sometimes that's not sufficient for obvious reasons. If so:
1593 * Basic fact: Next fastest is, on error, to return an Error_code (or just a `bool` or similar, if truly sufficient).
1594 * This is very fast; it's just like returning an `int` (it's a couple of `int`s really internally but close enough).
1595 * Basic fact: Finally, you can throw an exception. This should be considered slow, and I leave it at that for
1596 * simplicity.
1597 * HUGELY KEY FACT: *Not* throwing an exception (meaning no error was detected) is *just* as fast as returning
1598 * a success code / false / zero / etc. A tiny-tiny bit faster even. A *potential* exception -- or 500 potential
1599 * exceptions in a given call tree -- has zero perf cost, period.
1600 *
1601 * Therefore this somewhat vague guideline: Avoid exceptions in internal implementations, as then there's no perf
1602 * question. However, it may be very convenient to throw an exception sometimes; the stack unwinds and can make
1603 * some code so much simpler. Then, consider using an exception... and do so if you can very confidently state this
1604 * will *not* cause a high rate of exceptions thrown in reality. If there's any question about that, lean to avoiding
1605 * exception code paths after all. If there isn't, feel free. For example, as of this writing there are only a
1606 * small handful of such places in Flow, but they do exist.
1607 *
1608 * See also some notes on internally thrown exceptions in common.hpp. */
1609
1610// -- BEST PRACTICES: Logging --
1611
1612/* - Log as much as possible and practical. If verbosity+perf is a concern, just use the appropriate log::Sev. -
1613 *
1614 * Do have some concern for the visual experience of reading the logs (shouldn't be TOO redundant and wordy).
1615 * [Rationale: Debuggers are great... but not always available when you need them. Logging is superior to
1616 * relying on debuggers in almost every way; in many cases it even avoids the need to reproduce problem/bug, as it
1617 * can often be solved by looking at the logs. Even if not, it's much easier to turn up verbosity and reproduce than
1618 * to attach a debugger or examine a core file. The famous K&R C book makes this wonderful, correct point.] */
1619
1620// -- BEST PRACTICES: Types and type safety --
1621
1622/* - Do: Always ensure total const-correctness. -
1623 *
1624 * [Rationale: Probably most/all coders are familiar with this and likely agree. But this is a const-correct shop,
1625 * no exceptions, outside of working with ill-behaved outside libraries which may not be const-correct.] */
1626
1627/* - Corollary: Do not: use C-style casts; const|reinterpret_cast<> are to be rare and best justified w/ comment. -
1628 *
1629 * [Rationale: C-style cast, like `(T)x`, is always bad, as it allows type-unsafety and const-discarding without
1630 * even clearly doing so. Instead, use `static|dynamic|reinterpret|const_cast`, with the latter 2 used rarely
1631 * when it is required to be type-unsafe and const-incorrect respectively. Hence explain why you're doing it in those
1632 * cases.] */
1633
1634// @todo: Section here about aliasing/punning/object-vs-bytes/[u]intptr_t/etc. Working on the proper verbiage.
1635
1636/* - Do: Learn lambdas ASAP. Use them extensively. There should be NO reason to use bind() or a functor. -
1637 *
1638 * [Rationale: Can wax poetic here, but it's best understood by experience. One sees the advantages quickly.
1639 * Basically, lambdas are pithy, fast, and flexible ways to create function objects which is a constant need in
1640 * modern C++. bind() was SPECTACULAR in C++03, but with C++11 lambdas its use cases are mainly subsumed, adding
1641 * not only clarity but performance (compiler is much more likely to inline/optimize cleverly, as it's a built-in
1642 * language feature and not a crazy feature built in the library via C++ generic trickery).] */
1643// - Corollary: Async code flows can be expressed readably, helping resolve the biggest prob with async code flows. -
1644
1645// - Do not: Use #define macros for constants. Use typed `const`s. - [Rationale: Better for debugging, type safety.]
1646// - Corollary: Non-functional macros (#define without `()`) must be very rare, such as for an efficient logging API. -
1647
1648/* - Avoid: functional macros, period. -
1649 *
1650 * See if it can be done with a function with minimal loss of syntactic sugar. [Rationale: Debugging, type safety.]
1651 * If there is syntactic sugar lost that is absolutely essential, write as much as possible in a function; then wrap
1652 * that in a functional #define to add the necessary syntactic sugar. (Example: __FILE__, __LINE__, __FUNCTION__
1653 * are often auto-logged in logging APIs; a macro is needed to add these automatically at log call sites without
1654 * coder having to do so every time.) */
1655
1656// - Corollary: Functional macros used in a local chunk of one file should be #undef`ed. -
1657
1658// - Do: Use `const T&` for performance (instead of `T`) when relevant. -
1659
1660/* - Avoid: Using non-const `T&` function args (use T* instead). -
1661 *
1662 * Hence if you have an out-arg of type T, take a pointer to T, not a reference to T. (You can easily alias it to
1663 * a reference T& inside the function, if you like it for syntactic sugar.) If it can be null, you can use that as
1664 * a semantic tidbit meaning "turn off the feature to do with this arg"; if there's no need then just assert() it's not
1665 * null and move on.
1666 *
1667 * Hence, when calling a function and passing an out-arg, it'll commonly look like: cool_func(&modified_obj);
1668 * That's instead of: cool_func(modified_obj);
1669 *
1670 * [Rationale: This might be surprising, as vast majority of coders (including STL, Boost) use non-const ref out-args.
1671 * This guideline may seem controversial (and it is, after all, optional though strongly encouraged) but consider this:
1672 * - Using a pointer is no slower. assert() (if applicable) costs little to nothing.
1673 * - Using a pointer makes the call site *much* more expressive: You can see a thing may be modified.
1674 * Makes algorithm easier to understand at a glance.
1675 * - Using a pointer provides an easy way to "turn off" the arg which is commonly needed though of course not
1676 * universal. When so used, the API is usually pithier than the alternative (requiring another arg, or a special
1677 * value, or something). For example, see how Flow's lib-API-error-returning convention works:
1678 * if you pass in `Error_code* ec = nullptr`, it'll throw exception on error; else it'll return normally and set
1679 * *ec accordingly (including setting it no-error on success).
1680 * Try it. You might like it.]
1681 *
1682 * Not a hard rule. In particular, `ostream& os` is a common arg form by convention from STL and Boost. Use your
1683 * judgment. */
1684
1685// -- BEST PRACTICES: General design do's and don'ts --
1686
1687/* - Do: Use inheritance for "is-a" interfaces, as in Java interfaces (no, or very little, non-pure-virtual content). -
1688 *
1689 * Class A = interface; describes how to use it as *only* 1+ pure virtual methods.
1690 * Classes B, C = concrete types: Each B or C "is-an" A, so they use public inheritance B->A<-C. */
1691/* - Corollary: With wide-spread use of such interfaces, multiple inheritance becomes wide-spread... yet simple, as
1692 * one inherits only pure virtual method signatures and nothing else. - */
1693
1694/* - Do: Use inheritance when adding capabilities to an existing concrete class, a/k/a "is-a" inheritance. -
1695 *
1696 * Class A = concrete class with capabilies X.
1697 * Class B `public`ly inherits B->A and adds capabilities Y. Therefore, B "is-an" A, plus it adds more capabilities
1698 * on top. (This can of course continue indefinitely. C can subclass B and add more stuff; etc. */
1699
1700/* - Avoid: Using inheritance to provide common capabilities to 2+ subclasses. -
1701 * - Instead: Use composition. -
1702 *
1703 * Composition = put common capabilities into a separate class; then instantiate data member(s) and/or local
1704 * variable(s) of that type; no need to derive from that type usually.
1705
1706 * Two techniques are pretty common but should be avoided.
1707 * - Classes B and C need common, stateful capabilities in class A. Therefore, use `private` inheritance B->A<-C.
1708 * Instead: Instantiate A in B and C, as needed (composition).
1709 * - Classes B and C implement interface A. Therefore, use `public` B->A<-C... *but also* B and C need
1710 * common, stateful capabilities. Therefore, add those capabilities into A as well making it not a clean interface.
1711 * Instead: Keep the interface class A clean; create separate class A' with the common, stateful capabilities;
1712 * then instantiate A' in B and C, as needed (composition).
1713 *
1714 * *Not* a hard rule, but considering it before going ahead with non-interface-y inheritance = good design instinct.
1715 *
1716 * [Rationale: For me (ygoldfel), this lesson took 10+ years to learn. It is much more pleasant and flexible.
1717 * Using inheritance for this tends to be the result of not cleanly separating the notion of "common interface" (primary
1718 * purpose of inheritance) from that of "common concrete code" (typically easily done with composition).
1719 * There's also a certain beauty to a B->A<-C relationship that tightly helps B and C reuse complex code stored in A,
1720 * while B and C don't care about each other. What I've learned over a long time is that this satisfaction becomes
1721 * fleeting, when one has to tweak something about B->A or C->A, and suddenly one has to rejigger the whole setup.
1722 * Composition (where A is instantiated by each of B and C) helps make that process much simpler.
1723 * Among other things, A can be constructed/destroyed anytime; instantiated 2+ times simultaneously; etc.] */
1724
1725/* - Corollary: `protected` non-pure non-`static` methods and `const` data members are OK, but consider composition. -
1726
1727/* - Do not: use `protected` non-`const` data members (member of A, with inheritance B->A)! -
1728 *
1729 * This breaks encapsulation; A is best designed to be self-contained; giving direct mutable access to B allows it to
1730 * break invariants of A; so now basically A and B become inter-related and must be maintained together, tightly
1731 * coupled.
1732 *
1733 * Can't you just "cheat" by wrapping the access into a non-`const`-reference-returning `protected` accessor or
1734 * something? Well, yes, but then you're still breaking the spirit of the guideline. The spirit is the point.
1735 * Still, a protected method or accessor (if you must) is still better. */
1736
1737/* - Do: Use the `_base` inheritance pattern when a T-parameterized class template C contains non-T-parameterized
1738 * aliases/constants/etc. that must be usable without actually instantiating the template C<T> for some concrete
1739 * `T`. -
1740 *
1741 * For example, in STL, `std::ios_base::app` ("append" file mode constant) is used without having to mention a full-on
1742 * `basic_ostream<char, [...]>` (a/k/a `ostream`) nor any other such basic_ostream<> type. Note
1743 * basic_ostream<[...]> inherits from `ios_base`.
1744 *
1745 * Composition can also be often used. However, think of it like this: "Really I want to put the stuff right into
1746 * C<T>, where it belongs, but then people can't use it easily without instantiating the template, so I'll just split
1747 * it off into a _base class. I could just put the stuff entirely outside C<T>, but _base is a nicer pattern." */
1748
1749/* - Avoid: Adding public `static` methods into a class just because "everything should be in a class." -
1750 * - Instead: Is it really firmly linked conceptually to class C? No? Make it a free function then. -
1751 *
1752 * This isn't a hard rule at all. For example, factory `static` methods are very common, and that's fine.
1753 * But that's different, because in that case it's essentially a constructor and *is* firmly linked conceptually to
1754 * class C. Plus, then this method can access C's insides (data members even), and that can be very useful.
1755 *
1756 * [Rationale: This isn't a hard rule at all. Public `static` methods are common. However, people tend to use classes
1757 * as, basically, namespaces that organize groups of public `static` methods. This seems to be a result of the
1758 * pre-`namespace` era. Now, one can just make an actual `namespace` if such grouping is desired. Use judgment.] */
1759
1760/* - Avoid: Using `friend` to "cheat" an otherwise intentional encapsulation design. -
1761 * - Instead: Reconsider your design. -
1762 *
1763 * This is NOT saying `friend`s shouldn't be used just because. There are a few perfectly reasonable `friend` uses;
1764 * a common one is for binary operators written as free functions (`string operator+(string, string)` and such).
1765 * I won't try to enumerate them here. Just saying: If it feels like you're using `friend` as an "exception" to an
1766 * otherwise solid relationship between 2 encapsulating entities, then you might be abusing the existing design which
1767 * can easily lead to more abuse and a mess over time. Then, ask yourself if maybe that design can be amended. */
1768
1769# endif
1770#endif // !defined(FLOW_DOXYGEN_ONLY)
std::ostream & operator<<(std::ostream &os, const Option_set< Value_set > &val)
Serializes (briefly) an Option_set to a standard output stream.
#define FLOW_LOG_WITHOUT_CHECKING(ARG_sev, ARG_stream_fragment)
Identical to FLOW_LOG_WITH_CHECKING() but foregoes the filter (Logger::should_log()) check.
Definition: log.hpp:617
#define FLOW_LOG_WARNING(ARG_stream_fragment)
Logs a WARNING message into flow::log::Logger *get_logger() with flow::log::Component get_log_compone...
Definition: log.hpp:155
Scheduled_task_handle schedule_task_at(log::Logger *logger_ptr, const Fine_time_pt &at, bool single_threaded, Task_engine *task_engine, Scheduled_task_handler &&task_body_moved)
Identical to schedule_task_from_now() except the time is specified in absolute terms.
Definition: sched_task.hpp:246
Fine_clock::duration Fine_duration
A high-res time duration as computed from two Fine_time_pts.
Definition: common.hpp:405
Fine_clock::time_point Fine_time_pt
A high-res time point as returned by Fine_clock::now() and suitable for precise time math in general.
Definition: common.hpp:402
unsigned char uint8_t
Byte. Best way to represent a byte of binary data. This is 8 bits on all modern systems.
Definition: common.hpp:380