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