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