Flow 1.0.1
Flow project: Full implementation reference.
common.hpp
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/// @file
19#pragma once
20
22/* These are just so commonly used, that I (ygoldfel) decided to just shove them here for everyone because of the
23 * following boost.chrono I/O thing being nearby anyway. */
24#include <boost/chrono/chrono.hpp>
25#include <boost/chrono/ceil.hpp>
26#include <boost/chrono/round.hpp>
27/* boost.chrono I/O: This is subtle. chrono.hpp doesn't include I/O (including the invaluable ostream<< output of
28 * chrono::duration and chrono::time_point, especially when the latter is from chrono::system_clock). (Probably that
29 * is because std::chrono I/O support is spotty-to-non-existent, including lacking solid ostream<<.) One must include
30 * chrono/chrono_io.hpp... except, as it stands, this loads v1 -- not v2. (Looking inside the header shows it's based
31 * on some BOOST_ #define jungle, but the bottom line is every time I (ygoldfel) see Boost built, it loads v1, not v2.)
32 * However loading v2 is easy (and correct): #include the following 2 files instead; chrono_io.hpp would do so itself if
33 * the BOOST_ #define were set accordingly.
34 *
35 * This should be transparent, and transparently a good thing, for all `#include`ing devs. There are some subtleties
36 * however. In particular these bear mentioning:
37 * - Output of a time_point from system_clock (e.g., chrono::system_clock::now()) to an ostream will (in v2) yield a
38 * nice, predictably formatted, human-readable date/time string that includes the UTC +0000 specifier.
39 * (v1 said something fairly useless -- which v2 still, by necessity, does for non-system_clock values -- like
40 * "28374928742987 nanoseconds since Jan 1, 1970".) This is used directly by flow::log for example.
41 * With chrono::time_fmt(chrono::timezone::local) formatter applied to the ostream<<, it'll be in local time
42 * with the appropriate +xxxx specifier, instead. (There is also custom formatting available but never mind.)
43 * - Breaking change: `chrono::duration_{short|long}` don't work (v1); use chrono::{symbol|name}_format instead (v2).
44 * (This switches between, e.g., "5 ns" and "5 nanoseconds.")
45 * - As of Boost 1.76 at least, v2 includes some deprecated Boost headers which results in a #pragma message (not
46 * a warning/error but verbose and unsightly). -DBOOST_ALLOW_DEPRECATED_HEADERS will make it go away.
47 * - v1 and v2 cannot co-exist (some things, like ostream<<, become defined 2x). Therefore do not attempt to
48 * #include <boost/chrono/chrono_io.hpp>. At best it's redundant; at worst it will fail to compile. */
49#include <boost/chrono/io/duration_io.hpp>
50#include <boost/chrono/io/time_point_io.hpp>
51#include <functional>
52// For certains compile-time checks below.
53#ifdef __APPLE__
54# include <TargetConditionals.h>
55#endif
56
57/**
58 * @mainpage
59 *
60 * Welcome to the Flow project. The documentation you're viewing has been generated using Doxygen directly from Flow
61 * source code and comments. I recommend reading the documentation of `namespace` ::flow as the starting point; then
62 * that will direct one to the documentation of one or more of its sub-namespaces, depending on which Flow module(s)'s
63 * functionality interests that person.
64 */
65
66/* We build in C++17 mode ourselves (as of this writing), but linking user shouldn't care about that so much.
67 * The APIs and header-inlined stuff (templates, constexprs, possibly explicitly-inlined functions [though we avoid
68 * those]), however, also requires C++17 or newer; and that applies to the linking user's `#include`ing .cpp file(s)!
69 * Therefore enforce it by failing compile unless compiler's C++17 or newer mode is in use.
70 *
71 * Note this isn't academic; as of this writing there's at least a C++14-requiring constexpr feature in use in one
72 * of the headers. So at least C++14 has been required for ages in actual practice. Later, notched it up to C++17
73 * by similar logic. */
74#if (!defined(__cplusplus)) || (__cplusplus < 201703L)
75# error "To compile a translation unit that `#include`s any flow/ API headers, use C++17 compile mode or later."
76#endif
77
78// Macros. These (conceptually) belong to the `flow` namespace (hence the prefix for each macro).
79
80#ifdef FLOW_DOXYGEN_ONLY // Compiler ignores; Doxygen sees.
81
82/// Macro that is defined if and only if the compiling environment is Linux.
83# define FLOW_OS_LINUX
84
85/// Macro that is defined if and only if the compiling environment is Mac OS X or higher macOS (not iOS and such!).
86# define FLOW_OS_MAC
87
88/// Macro that is defined if and only if the compiling environment is Windows.
89# define FLOW_OS_WIN
90
91#else // if !defined(FLOW_DOXYGEN_ONLY)
92
93// Now the actual definitions compiler sees (Doxygen ignores).
94
95/* Used this delightful page for this logic:
96 * https://web.archive.org/web/20140625123925/http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system */
97# ifdef __linux__
98# define FLOW_OS_LINUX
99# elif defined(__APPLE__) && (TARGET_OS_MAC == 1)
100# define FLOW_OS_MAC
101# elif defined(_WIN32) || defined(_WIN64)
102# define FLOW_OS_WIN
103# endif
104
105#endif // elif !defined(FLOW_DOXYGEN_ONLY)
106
107/**
108 * Catch-all namespace for the Flow project: A collection of various production-quality modules written in modern
109 * C++17, originally by ygoldfel. (The very first version was in Boost-y C++03, back around 2010. Later, ~2019, it
110 * moved to C++14 in style and substance; and in 2022 to C++17 -- a relatively minor upgrade.)
111 * While the modules are orthogonal to each other in terms of functionality
112 * provided, they all share a common set of stylistic conventions and happen to use each other internally; hence
113 * they are distributed together as of this writing.
114 *
115 * From the user's perspective, one should view this namespace as the "root," meaning it consists of two parts:
116 * - Sub-namespaces (like flow::log, flow::util, flow::async, flow::net_flow),
117 * each of which represents a *Flow module* providing certain
118 * grouped functionality. Each module is self-contained from the point of view of the user, meaning:
119 * While the various modules may use each other internally (and hence cannot be easily distributed separately), from
120 * user's perspective each one can be directly `include`d/referenced without directly `include`ing/referring to the
121 * others. E.g., one can directly reference `namespace log` *and/or* `namespace util` *and/or* `namespace net_flow`
122 * *and/or* .... Further documentation can be found in the doc headers for each individual sub-namespace.
123 * - Symbols directly in `flow`: The absolute most basic, commonly used symbols (such as `uint32_t` or `Error_code`).
124 * There should be only a handful of these, and they are likely to be small.
125 * - In particular this includes `enum class Flow_log_component` which defines the set of possible
126 * flow::log::Component values logged from within all modules of Flow (again, including flow::util, flow::async,
127 * flow::net_flow, etc.). See end of common.hpp.
128 *
129 * Reiterating: Non-`namespace` symbols directly inside `namespace flow` are to be only extremely ubiquitous items
130 * such as the basic integer types and the log component `enum`. Anything beyond that should go into a sub-`namespace`
131 * of `flow`; if it is something miscellaneous then put it into flow::util.
132 *
133 * Here we summarize topics relevant to all of Flow. It is recommend one reads this before using any individual module,
134 * for topics like file organization and style conventions, topics arguably not of huge practical value right away.
135 * However, all the actual functionality is in the sub-modules, so once you're ready to actually do stuff, I reiterate:
136 * See the list of sub-namespaces of `flow` and start with the doc header of the one(s) of interest to you.
137 *
138 * Documentation / Doxygen
139 * -----------------------
140 *
141 * All code in the project proper follows a high standard of documentation, almost solely via comments therein
142 * (as opposed to ancillary doc files/READMEs/etc.). Additionally, a subset of comments are Doxygen-targeted,
143 * meaning the comment starts with a special character sequence to cause the Doxygen utility to treat that comment
144 * as a doc header for a nearby symbol (class, method, etc.). (If you're not familiar with Doxygen: It's like Javadoc
145 * but better -- although mostly compatible with Javadoc-targeted comments as well -- not that we care about that.)
146 *
147 * From the same source code (the entire Flow tree) 2 (two) web docs are generated by Doxygen utility -- depending on
148 * which of the 2 Doxygen configuration files is chosen. You can determine which web docs you're reading currently
149 * (if indeed you are doing that, as opposed to reading raw code) via the wording of the title/header in every web page:
150 * - *Flow project: Full implementation reference*: This scans every header and translation unit file, including
151 * those in detail/ sub-directories at all levels; and all Doxygen-targeted comments (including this one)
152 * are fully scanned. Full browsable source code is included in the output also. As a result, it's basically
153 * an ultra-nice viewer of the entire source code (implementation included), with special emphasis on
154 * doc headers such as this one.
155 * - *Flow project: Public API*: Only headers are scanned; and all detail/ sub-directories at all levels are ignored.
156 * Additionally, any text following the (optional) `"@internal"` Doxygen command within each given Doxygen comment
157 * is ignored. Browsable source code is omitted. As a result, a clean public API documentation is generated with
158 * no "fat."
159 *
160 * While an important (achieved) goal is to keep the documentation of the library (internal and external, as noted)
161 * self-contained -- within the source code or auto-generated by taking that source code as input -- nevertheless some
162 * satellite documentation is likely to exist; for example to cover such things as the logistical state of the project,
163 * its test harness, perhaps the license, etc. Look outside the root src/flow directory.
164 *
165 * @todo As of this writing the *exact* nature of where the project will permanently reside (and who will maintain it
166 * vs. use it) is in flux. Therefore for now I have removed the section covering certain topics and replaced it with
167 * the to-do you're reading. This should be undone when things settle down (obviously ensuring the brought-back section
168 * is made accurate). The topics to cover: `"@author"` (including contact info); GitHub/other address indicating where
169 * to browse the project source; link(s) to the latest auto-generated web docs (if any); a section on the
170 * history of the project; and licensing info (if deemed needed) or pointer to it. (Reminder: Also update any
171 * similar sections of the historically significant net_flow::Node doc header.)
172 *
173 * @todo Since Flow gained its first users beyond the original author, some Flow-adjacent code has been written from
174 * which Flow can benefit, including a potential `io` module/namespace for general networking/local I/O.
175 * (Flow itself continued to be developed, but some features were added
176 * elsewhere for expediency; this is a reminder to factor them out into Flow for the benefit of
177 * all.) Some features to migrate here might be: boost.asio extensions to UDP receive APIs to obtain receipt time
178 * stamps and destination IP (`recvmsg()` with ancillary data extensions) and to receive multiple datagrams in one
179 * call (`recvmmsg()`); boost.asio-adjacent facility to add custom socket options similarly to how boost.asio does it
180 * internally; boost.asio support for (local) transmission of native socket handles and security data over stream
181 * sockets (`SCM_RIGHTS`, etc.).
182 *
183 * Using Flow modules
184 * ------------------
185 *
186 * This section discusses usability topics that apply to all Flow modules including hopefully any future ones but
187 * definitely all existing ones as of this writing.
188 *
189 * ### Error reporting ###
190 * Similarly to boost.asio, all public methods that may return errors can either do so via an
191 * error code or an exception encapsulating that same error code. If user passes in non-null pointer to a
192 * flow::Error_code variable, the latter is set to success (falsy) or a specific failure (truthy `enum` value). If
193 * user passes in null pointer, an exception `exc` is thrown only in case of error and will encapsulate that error code
194 * (accessible via `exc.code()`).
195 *
196 * For details about error reporting, see doc headers for flow::Error_code (spoiler alert: a mere alias to
197 * `boost::system::error_code`) and `namespace` flow::error.
198 *
199 * ### Logging ###
200 * The flow::log namespace (see especially log::Logger and the various `FLOW_LOG_...*()` macros in
201 * log/log.hpp) provides a logging facility -- used by Flow modules' often-extensive logging, and equally available
202 * to the Flow user. Ultimately, you may tweak the log level and then observe the given Flow module's internal behavior
203 * to whatever level of detail you desire. Similarly, as the user, you may use this system for your own logging,
204 * even if you use no Flow module other than flow::log itself. Either way, you can hook up flow::log to log via your
205 * own log output device in arbitrary fashion (e.g., save the log messages in a database, if that's what you want).
206 *
207 * For details about logging, see doc header for `namespace` flow::log.
208 *
209 * @internal
210 *
211 * Implementation notes
212 * --------------------
213 *
214 * There is a high standard of consistency and style, as well as documentation, in Flow. Before making changes to
215 * the code, you must read doc-coding_style.cpp.
216 *
217 * ### Source file tree organization ###
218 * See doc-coding_style.cpp which covers this topic.
219 *
220 * ### Libraries used; Boost ###
221 * We use STL and several Boost libraries (notably boost.asio) extensively.
222 * See doc-coding_style.cpp which covers this topic.
223 *
224 * ### Exceptions ###
225 * For reasons of speed (hearsay or reputational though they may be) we avoid exception-throwing
226 * boost.asio routines inside the modules' implementations, even though using them would make the code a bit simpler
227 * in many areas. Similarly, we avoid throwing our own exceptions only to catch them higher in the back-trace.
228 * In both cases, we typically use error codes/return values. These are not absolute/hard rules, as exceptions may be
229 * used where minuscule speed improvements are immaterial (like the initialization and shutdown of a long-lived object
230 * such as net_flow::Node).
231 *
232 * After a recent look at performance implications of exceptions, the following is the basic conclusion: Exceptions have
233 * no processor cycle cost, unless actually thrown, in which case the perf cost can be non-trivial. Since we consider
234 * internal performance of paramount importance, we generally avoid throwing exceptions -- only to catch them
235 * internally -- as noted in the preceding paragraph. Arguably such situations are rare anyway, so using an exception
236 * internally wouldn't actually slow anything down, but we haven't performed a code audit to verify that stipulation;
237 * and in the meantime the consistent avoidance of using internally caught exceptions has proved to be a very reasonable
238 * policy. (For some modules this may be arguably mostly moot: Consider flow::net_flow specifically: Since --
239 * internally -- mostly boost.asio `async_*()` methods are used, and those
240 * by definition report errors as codes passed into async handler functions; whether to use an exception or not is
241 * an ill-formed question in all such situations.) We recommend this practice continue, until there's a specific reason
242 * to reconsider.
243 *
244 * See doc-coding_style.cpp for more discussion of error handling and exceptions.
245 *
246 * Code style guidelines
247 * ---------------------
248 *
249 * ### General coding style requirements ###
250 * The formal guidelines are in doc-coding_style.cpp; read that file please.
251 *
252 * ### Documentation guidelines ###
253 * The standard for documentation of Flow modules is that someone reading the source code, and nothing else, would
254 * be able to understand that code (modulo having the intellectual sophistication/experience w/r/t the subject
255 * matter, of course). Simple but quite a task given how much code there is and the complexity. We also
256 * produce Doxygen output (2 web pages, as of this writing) by running the code through Doxygen.
257 *
258 * @see More on the technicalities of how we run Doxygen, and the philosophy behind all of that, can be found
259 * in a `doc/` or similar directory outside src/flow. It's rare something pertinent to the source code
260 * is not IN the source code (i.e., right here somewhere), but the README explains why that rare choice is
261 * made (among many more practical/interesting things). This is worth reading if you'll be contributing to the
262 * code.
263 *
264 * The actual guidelines are, as above, in doc-coding_style.cpp; read that file please.
265 *
266 * @todo Possibly document exceptions thrown explicitly via the Doxygen keyword meant for this purpose: `"@throws"`.
267 * Currently when we document explicitly throwing an exception, it is ALWAYS a flow::error::Runtime_error
268 * encapsulating a flow::Error_code (which is an `int`-like error code). This is very explicitly documented,
269 * but technically Doxygen has a keyword which will generate a special little readout for the exception
270 * (similarly as for each parameter, etc.). We don't use that keyword. We probably should, though this
271 * isn't totally cut-and-dried. Consider that we already document the exception on the `err_code` parameter
272 * in every case; so no information would really be gained (only arguably nicer formatting). On the other hand,
273 * the code would be somewhat more verbose (and boiler-platey, since each already boiler-platey `err_code`
274 * comment snippet would essentially grow in size). Furthermore, if we document this explicit exception, one might
275 * say it behooves us to now document all the other possible sources of exceptions such as `std::bad_alloc` when
276 * running out of heap memory. Perhaps then we have to talk about constructor-throwing-exception behavior and
277 * other C++ technicalities to do with exceptions. Do we really want to enter that land? I think maybe not;
278 * consider just leaving it alone. Though, maybe I'm over-dramatizing the impact of adding a `"@throws"`
279 * section on our various flow::error::Runtime_error-throwing methods. Well, it's a to-do; decide later.
280 *
281 * To-dos and future features
282 * --------------------------
283 *
284 * @todo The comments (as of this writing, all written by me, ygoldfel) in this library could use an edit to make
285 * them briefer. (I've found even a self-edit by me, with that express purpose, often does wonders.) Background:
286 * I write very extensive comments. I am quite convinced this is superior (far superior even) to next-to-no comments;
287 * and to the average amount of comments one tends to see in production code. *That said*, the top code review
288 * feedback I receive (not here specifically but in general) is that my comments tend to be too "discursive" (consisting
289 * of discourse) and/or at times unnecessarily in-depth. Discursive = as if I am talking to the reader (many prefer
290 * a terser, more formal style). Too in-depth = tends to go into history, related issues, future work, etc.
291 * (these elements can remain but can be cut down significant without losing much substance).
292 * In other words, there should be a happy middle in terms of comment volume, and this can
293 * be achieved by a comment edit run by Yuri or someone else (if reviewed by Yuri). To be clear, I believe this middle
294 * ground is to be closer to the status quo than to the average amount of comments in other projects.
295 *
296 * @todo Be more specific (cite date) when writing "as of this writing."
297 * I use a rhetorical trick when commenting the state of something that may not continue to be the case.
298 * Though I do avoid writing such things, often it is necessary; in that case I usually write "as of this writing" or
299 * something very similarly worded. That's fine and essentially the best one can do. It means technically the
300 * statement won't become false, even if the "sub-statement" (the thing that was true when written) does become false.
301 * However, obviously, to the reader of the comment at that later time, that's little consolation: they're still reading
302 * a possibly false statement and will want to know what the situation is THEN, or "as of the reading," to to speak.
303 * In order to at least try to be helpful, in those cases a date (numeric month/year -- like 4/2017 -- should be
304 * sufficient in most cases) should be supplied. The to-do is to convert all "as of this writing" instances -- and
305 * to always add a date when writing new instances of "as of this writing." The to-do can be removed once the
306 * conversion is completed. Example: this to-do has not been completed as of this writing (11/2017).
307 * (Side note: possibly goes without saying, but one is free to explain to an arbitrary degree of detail why something
308 * is true as of that writing, and how/why/when it might change. This to-do covers those cases where no such
309 * explanation is written. It would be impractically verbose to get into speculative detail for every
310 * as-of-this-writing instance; so at least a date should thus be inserted.)
311 *
312 * @todo There are some boost.thread "interruption points" throughout the code, so we should
313 * investigate whether we must catch `boost::thread_interrupted` in those spots, or what...?
314 *
315 * @todo Inline things: Or just use `gcc -O3` (and non-`gcc` equivalents) for prettier/faster-to-compile
316 * code? The latter is definitely tempting if it works sufficiently well. So far we've been using
317 * `gcc -O3` and equivalents, and it seems to be working well (turning off inlining results in huge
318 * performance losses). Still, I am not sure if it would be better to explicitly `inline` functions
319 * instead. Not having to do so definitely simplifies the code, so it is my great hope that the answer is
320 * no, and we can keep using `gcc -O3` and equivalents. In that case delete this paragraph.
321 * (To be clear: `gcc -O3` means that it ignores `inline` keyword and anything similar, including inlined
322 * method bodies inside `class {}` and `struct {}`. Instead it determines what to inline based on its own
323 * ideas on what will generate the fastest code (with reasonable code size). `gcc -O2`, on the other
324 * hand, will mostly inline things explicitly declared as such in code (again, via `inline` or inlining inside
325 * class bodies or other techniques).) Update: Now using clang (not gcc) with maximum auto-inlining AND FLTO
326 * (link-time optimization will allow inlining across object file boundaries) in at least some platforms.
327 * This should be close to as good as possible. Update: gcc auto-inlining+FLTO also works.
328 *
329 * @todo One space after period, not two:
330 * For some reason in this project I've been following the convention -- in comments and (I think) log
331 * messages -- of two spaces after a sentence-ending punctuator (usually period) before the next sentence's
332 * first character. I now regret trying this annoying convention. Go back to one space. (For the record,
333 * it does look sort of nice, but that's a matter of opinion, and single space looks fine too... AND doesn't
334 * confuse various editors' auto-formatting facilityies, among other problem.)
335 *
336 * @todo We use `0` instead of `NULL` or `nullptr` when needing a null pointer; perhaps we should use the latter.
337 * `NULL` is an anachronism from C, so we shouldn't use it. `nullptr` is at least no worse than `0`,
338 * however, other than being less concise. However, the main reason it exists --
339 * to avoid ambiguities in function overloading (e.g., when something could
340 * take either an `int` or a `char*`, `nullptr` would resolve to the latter, while `0` probably unintentionally
341 * to the former) -- is not a situation our style ever invokes, to my knowledge, so using `nullptr` would not
342 * solve any actual problems. However, it could be argued that using it more readily calls attention to the use
343 * of a pointer, as opposed to an integer, in the particular context at play. So it's something to consider
344 * (but, no doubt, the conversion process would be laborious, as there's no simple search-replace that would
345 * work).
346 *
347 * @todo `= default` for copy constructors and copy operators is now used in a few places; consider spreading
348 * this C++11 feature everywhere it's being done implicitly due to C++03 rules (good documentation practices suggest
349 * declaring them explicitly but of course leave the implementation to the compiler default, gaining best of both
350 * worlds -- proper class API docs yet maintenance-friendly default body).
351 *
352 * @todo Consider PIMPL and related topics. Recommend scouring Boost docs, particularly for the smart pointer library,
353 * which discuss how to potentially use smart pointers for easy PIMPLing. In general, research the state of the art
354 * on the topic of library interface vs. implementation/hiding.
355 *
356 * @todo `std::string_view` is a way to pass things around similarly to `const std::string&` without requiring
357 * that a `string` be created just for that purpose; it has a highly similar API but can be constructed from any
358 * character sequence in memory and internally stores nothing more than a pointer and length; we should use it wherever
359 * possible (within reason) instead of `const std::string&`. Much code now uses `String_view`; the remaining to-do
360 * is: scour the rest of the code for possible `const string&`s to convert and indeed convert those to
361 * util::String_view.
362 *
363 * @todo Return-by-copy binary operators of the form `T operatorBLAH(const T& x1, const Some_type& x2)` should be
364 * written as free functions instead of `T` members. I don't recall at this point why, but this tends to be recommended
365 * and done in STL and Boost. Maybe check the Meyer Effective C++ book on the theory; and if it makes sense find all
366 * such operators written as members and change them to be free functions. Should this be avoided if it requires
367 * `friend` though? Lastly, for Doxygen, use the `relatesalso T` command to link the free function to the class `T`
368 * in the documentation.
369 *
370 * @todo In many (most) cases we pass `shared_ptr`s (and their aliases) by value when it would be more performant to
371 * do so by `const` reference; at times possibly better to pass by raw pointer. Scott Meyer/Herb Sutter have opined
372 * on this to basically indicate that (1) it is often best to use a raw pointer, unless actual copying/ownership status
373 * is expected; but failing that (2) it is often best to use a `const&` when safe; and failing that passing by
374 * value is fine. This need not be a dogmatic change, but we should be more mindful than simply always passing by
375 * value. When searching for instances to potentially change, check for `shared_ptr`, `Ptr`, and `_ptr` tokens.
376 */
377namespace flow
378{
379
380// Types. They're outside of `namespace ::flow::util` for brevity due to their frequent use.
381
382// Integer short-hands and specific-bit-width types.
383
384/// Byte. Best way to represent a byte of binary data. This is 8 bits on all modern systems.
385using uint8_t = unsigned char;
386/// Signed byte. Prefer to use `uint8_t` when representing binary data. This is 8 bits on all modern systems.
387using int8_t = signed char;
388
389// Time-related short-hands.
390
391/**
392 * Clock used for delicate time measurements, such that the `now()` method gets the current time
393 * relative to some unknown but constant epoch (reference point). Used to measure durations of
394 * things. It has the following properties:
395 *
396 * - Steady: time cannot go backwards (e.g., via user time change, NTP); time values increment at
397 * a rate proportional to real time (no leap seconds for example).
398 * - High-resolution: the increments of time at which the clock runs are as small as supported
399 * by the OS+hardware. This should be at most a handful of microseconds in practice.
400 *
401 * So basically it's a precise clock with no surprises (which is more than can be said for stuff people
402 * tend to be forced to use, like `gettimeofday()`).
403 */
404using Fine_clock = boost::chrono::high_resolution_clock;
405
406/// A high-res time point as returned by `Fine_clock::now()` and suitable for precise time math in general.
407using Fine_time_pt = Fine_clock::time_point;
408
409/// A high-res time duration as computed from two `Fine_time_pt`s.
410using Fine_duration = Fine_clock::duration;
411
412/**
413 * Short-hand for a boost.system error code (which basically encapsulates an integer/`enum` error
414 * code and a pointer through which to obtain a statically stored message string); this is how Flow modules
415 * report errors to the user; and we humbly recommended all C++ code use the same techniques.
416 *
417 * @note It is not inside flow::error namespace due to its (`Error_code`'s) ubiquity.
418 * Very few other symbols should follow suit. We may decide to move it there after all.
419 *
420 * ### Basic error-emitting API semantics ###
421 *
422 * All error-reporting Flow APIs follow the following pattern of error reporting semantics.
423 * Each API looks something like:
424 *
425 * ~~~
426 * return_type Some_class::some_op(..., flow::Error_code* err_code)
427 * ~~~
428 *
429 * Then, there are two possibilities. If you pass in non-null `err_code`, then after return `*err_code` is
430 * success (falsy) or a truthy `enum`-like value, representing a specific error. If, instead, you pass in null,
431 * then a flow::error::Runtime_error() `exc` is thrown if and only if `*err_code` would have been set to truthy value
432 * `e_c` had a non-null `err_code` been passed in. If such an exception is thrown, `Error_code e_c` is
433 * encapsulated in exception object `exc`. If and only if no exception is thrown, there was no error (`*err_code` would
434 * have been falsy).
435 *
436 * Thus, you get the best of both worlds: you can get the simplicity and performance
437 * of an error code; or the various features of an exception (including access to the error code via
438 * `exc.code()` if desired), with the same API signature. (boost.asio
439 * follows a similar concept, though it requires two API signatures for each operation, one without
440 * an `Error_code` argument, and one with non-`const` `Error_code&` out-arg. The above convention is more compact;
441 * plus we provide certain tools to reduce boiler-plate in connection with this.)
442 *
443 * ### Intro to `Error_code`, a/k/a boost.system `error_code` ###
444 * (I am restating boost.system documentation here, but that particular set of docs is notoriously
445 * formal-but-reader-unfriendly.)
446 *
447 * A truthy `Error_code` is a very lightweight -- `errno`-like in that regard -- value indicating
448 * the error that occurred. It stores an `int` code and a "category" pointer (basically, thing specifying to what code
449 * set this belongs). The `int` is to be converted from the error code set of choice, whereas the category pointer is
450 * internally magically determined based on the type of the error code value being converted to `Error_code`.
451 *
452 * An `Error_code` itself can be serialized into `ostream`s (and thus `string`s via `lexical_cast`, etc.) easily for
453 * logging purposes/etc. You can access both the numeric code and a human explanation of the error.
454 * Any and all error code sets are supported by this boost.system type. POSIX `errno`s are one possible set of codes;
455 * boost.asio has its own code set; and other modules in `flow` may introduce their own code sets. All are compatible
456 * for equality/assignment/etc. with this general `Error_code` type.
457 *
458 * As stated, all error-emitting Flow public APIs (regardless of module) use the above-described error-reporting
459 * conventions. In addition, we humbly recommend Flow *user* code adopt the same battle-tested conventions. However
460 * that is absolutely not required and is entirely independent of the fact that Flow modules use them. Do note this
461 * convention is battle-tested in boost.asio as well; though Flow's version is more compact; by using a pointer (which
462 * can be null) instead of a reference it cuts the number of error-emitting API functions in half.
463 *
464 * For each function (including each publicly exported error-reporting function within Flow) that indeed agrees to
465 * use the above convention, follow these instructions:
466 *
467 * To reduce boiler-plate, within reason, it is incumbent on each error-reporting method to use the following
468 * technique:
469 *
470 * - The method signature should be similar to the above (including naming it `err_code`) and use the above semantics.
471 * - Use FLOW_ERROR_EXEC_AND_THROW_ON_ERROR() (and/or nearby similar utilities in flow/error/error.hpp) for minimal
472 * boiler-plate that implements these semantics. See doc header for that macro for details.
473 * - You may or may not indicate the lack or presence of an error condition via some additional non-exception technique
474 * such as a `bool` return value.
475 * - The error behavior documentation should be *confined entirely* to the documentation of `err_code` parameter, so
476 * that the above semantics need not be repetitively restated a million times.
477 * The text of the parameter's doc should usually be as follows (you may copy/paste to start). In this example
478 * the function returns codes from the `net_flow::error::Code` code set `enum`; but please substitute your code set of
479 * choice (again; `errno` and boost.asio error codes are 2 possible other examples of code sets). Here we go:
480 *
481 * ~~~
482 * // param err_code
483 * // See flow::Error_code docs for error reporting semantics. net_flow::error::Code generated:
484 * // net_flow::error::Code::S_(...) (optional comment), ...more... , net_flow::error::Code::S_(...)
485 * // (optional comment).
486 * ~~~
487 *
488 * @see The doc header (and code inside) `namespace` flow::net_flow::error is a good primer showing how to create
489 * an `Error_code`-compatible set of error codes. This is easier to understand than boost.asio's counterpart
490 * for example.
491 *
492 * @note boost.system at some point -- I (ygoldfel) am fairly sure after I designed the above ages ago -- introduced
493 * an alternate idiom for passing an #Error_code out-arg that is to be ignored in favor of throwing an exception
494 * if omitted. We use the idiom: `Error_code*` out-arg, throw if null. They, instead propose:
495 * `Error_code&` out-arg, throw if it equals `boost::system::throws()`. That's great, too, but actually our
496 * idiom hews to another bit of the Flow coding style/guide, wherein out-args should be pointers, not
497 * non-`const` references -- and is otherwise very similar. So it's fine. Note that their idiom vs. ours =
498 * orthogonal to the main difficulty which is the boiler-plate associated with actually throwing vs. non-throwing;
499 * this would be required regardless of the API idiom chosen. The above (involving
500 * FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(), etc.) is really the main crux of it.
501 */
502using Error_code = boost::system::error_code;
503
504// See just below.
505template<typename Signature>
507
508/**
509 * Intended as the polymorphic function wrapper of choice for Flow, internally and externally; to be used
510 * instead of `std::function` or `boost::function`. Due to ubiquitous use of such function-object wrappers,
511 * this is one of the very few direct non-`namespace` members of the outer namespace `::flow`.
512 *
513 * In reality it *is* `std::function`, with a couple of added APIs (no data) to make it more similar to
514 * `boost::function` API-wise.
515 *
516 * ### Rationale ###
517 * By far the main reason this exists is: I (ygoldfel) conducted an investigation in 2022, with a not-too-old gcc
518 * in Linux, in C++17 mode, with Boost 1.78 (but built seemingly in C++03-supporting config) about the
519 * performance behavior of lambdas objects and `boost::function<>` and `std::function<>` wrappers thereof.
520 * I noticed these things:
521 * - Say one constructs a `boost::function` from a lambda that has 1 or more by-value captures `[x = std::move(x)]`,
522 * with those captures' types having move ctors different and faster than copy ctors. I found that this not only
523 * *copies* each of those captures (invokes their copy ctors), but it even does so several times!
524 * - However, constructing an `std::function` identically never invokes copy ctors -- only move ctors --
525 * and fewer times at that.
526 * - Say one constructs a `boost::function` from a lambda that has 1 or more by-value captures `[x = std::move(x)]`,
527 * with those captures' types having move ctors but `= delete`d copy ctors. I.e., say at least 1 capture is
528 * movable but not copyable; `unique_ptr` being a classic and practical example. Well: This does not compile;
529 * gcc complains `boost::function` needs `unique_ptr` to have a copy ctor, but it is `delete`d.
530 * In the case of `unique_ptr`, one can do `[x = shared_ptr(std::move(x))]` to get it work, though it's a bit
531 * more code, reduces perf by adding ref-counting, and reduces smart-ptr safety inside the lambda body.
532 * - However, constructing an `std::function` identically compiles and works fine.
533 *
534 * So, long story short, at least in that environment, `std::function` is just plain faster than `boost::function`,
535 * avoiding copying of captures; and it's easier to use with movable-not-copyable capture types.
536 *
537 * So we could have just done circa `using Function = std::function;`. Why the subclass? Answer: `std::function`
538 * lacks a couple of commonly used `boost::function` APIs that code tends to rely on (in the past we used
539 * `boost::function`). These are empty() and clear() as of this writing. See their doc headers (nothing amazing).
540 *
541 * Lastly: In the aforementioned environment, possibly because of having to support C++03 (which lacked
542 * param packs) -- unlike `std::function` which was introduced in C++11 to begin with (and
543 * probably conceptually based on `boost::function`) -- `boost::function`
544 * supports up to 10 args and does not compile when used with 11+ args. `std::function`, and therefore Function,
545 * lacks this limitation. It can be used with any number of args.
546 *
547 * @tparam Result
548 * See `std::function`.
549 * @tparam Args
550 * See `std::function`.
551 */
552template<typename Result, typename... Args>
553class Function<Result (Args...)> :
554 public std::function<Result (Args...)>
555{
556public:
557 // Types.
558
559 /// Short-hand for the base. We add no data of our own in this subclass, just a handful of APIs.
560 using Function_base = std::function<Result (Args...)>;
561
562 // Ctors/destructor.
563
564 /// Inherit all the constructors from #Function_base. Add none of our own.
565 using Function_base::Function_base;
566
567 // Methods.
568
569 /**
570 * Returns `!bool(*this)`; i.e., `true` if and only if `*this` has no target.
571 *
572 * ### Rationale ###
573 * Provided due to ubuiquity of code that uses `boost::function::empty()` which `std::function` lacks.
574 *
575 * @return See above.
576 */
577 bool empty() const noexcept;
578
579 /**
580 * Makes `*this` lack any target; i.e., makes it equal to a default-constructed object of this type, so that
581 * `empty() == true` is a post-condition.
582 *
583 * ### Rationale ###
584 * Provided due to ubuiquity of code that uses `boost::function::clear()` which `std::function` lacks.
585 */
586 void clear() noexcept;
587}; // class Function<Result (Args...)>
588
589#ifdef FLOW_DOXYGEN_ONLY // Actual compilation will ignore the below; but Doxygen will scan it and generate docs.
590
591/**
592 * The flow::log::Component payload enumeration comprising various log components used by Flow's own internal logging.
593 * Internal Flow code specifies members thereof when indicating the log component for each particular piece of
594 * logging code. Flow user specifies it, albeit very rarely, when configuring their program's logging
595 * such as via flow::log::Config::init_component_to_union_idx_mapping() and flow::log::Config::init_component_names().
596 *
597 * If you are reading this in Doxygen-generated output (likely a web page), be aware that the individual
598 * `enum` values are not documented right here, because flow::log auto-generates those via certain macro
599 * magic, and Doxygen cannot understand what is happening. However, you will find the same information
600 * directly in the source file `log_component_enum_declare.macros.hpp` (if the latter is clickable, click to see
601 * the source).
602 *
603 * ### Details regarding overall log system init in user program ###
604 *
605 * The following is a less formal reiteration of flow::log::Config documentation and is presented here -- even
606 * though technically in the present context Flow itself is nothing more than yet another module that uses
607 * flow::log for its own logging -- for your convenience. Flow's own logging can be seen as the canonical/model
608 * use of flow::log, so other flow::log users can read this to learn the basics of how to configure loggingg.
609 * That's why we re-explain this info here, in brief form:
610 *
611 * Your program -- that uses the present library -- can register this `enum` in order for these components
612 * (and particularly the log messages that specify them via flow::log::Log_context or
613 * FLOW_LOG_SET_CONTEXT()) to be logged properly in that program, co-existing correctly with other code bases
614 * that use flow::log for logging. Typically one constructs a `flow::log::Config C` and then at some point
615 * before logging begins:
616 * - For each `enum class X_log_component` (note that `Flow_log_component` is only one such `enum class`):
617 * -# `C.init_component_to_union_idx_mapping<X_log_component>(K)`, where `K` is a distinct numeric offset,
618 * maybe multiple of 1000.
619 * -# `C.init_component_names<X_log_component>(S_X_LOG_COMPONENT_NAME_MAP, ..., "X-")`.
620 * - Note the "X-" prefix, allowing one to prepend a namespace-like string prefix to avoid any output and config
621 * clashing.
622 * -# `C.configure_default_verbosity(Sev::S)`, where `S` is some default max severity.
623 * -# For each component `M` for which one desires a different max severity `S`:
624 * - `C.configure_component_verbosity<X_log_component>(Sev::S, X_log_component::M)`. OR:
625 * - `C.configure_component_verbosity_by_name(Sev::S, "X-M")`.
626 * - Apply `C` to the flow::log::Logger or `Logger`s you want to affect.
627 * - Pass the `Logger` or `Logger`s to appropriate APIs that want to log.
628 *
629 * One could make changes after logging has begun, but that's a separate topic.
630 */
632{
633 /**
634 * CAUTION -- see flow::Flow_log_component doc header for directions to find actual members of this
635 * `enum class`. This entry is a placeholder for Doxygen purposes only, because of the macro magic involved
636 * in generating the actual `enum class`.
637 */
639};
640
641/**
642 * The map generated by flow::log macro magic that maps each enumerated value in flow::Flow_log_component to its
643 * string representation as used in log output and verbosity config. Flow user specifies, albeit very rarely,
644 * when configuring their program's logging via flow::log::Config::init_component_names().
645 *
646 * As a Flow user, you can informally assume that if the component `enum` member is called `S_SOME_NAME`, then
647 * its string counterpart in this map will be auto-computed to be `"SOME_NAME"` (optionally prepended with a
648 * prefix as supplied to flow::log::Config::init_component_names()). This is achieved via flow::log macro magic.
649 *
650 * @see flow::Flow_log_component first.
651 */
652extern const boost::unordered_multimap<Flow_log_component, std::string> S_FLOW_LOG_COMPONENT_NAME_MAP;
653
654#endif // FLOW_DOXYGEN_ONLY
655
656// Template implementations.
657
658template<typename Result, typename... Args>
659bool Function<Result (Args...)>::empty() const noexcept
660{
661 return !*this;
662}
663
664template<typename Result, typename... Args>
665void Function<Result (Args...)>::clear() noexcept
666{
667 *this = {};
668}
669
670} // namespace flow
std::function< Result(Args...)> Function_base
Short-hand for the base. We add no data of our own in this subclass, just a handful of APIs.
Definition: common.hpp:560
Catch-all namespace for the Flow project: A collection of various production-quality modules written ...
Definition: async_fwd.hpp:75
boost::system::error_code Error_code
Short-hand for a boost.system error code (which basically encapsulates an integer/enum error code and...
Definition: common.hpp:502
Flow_log_component
The flow::log::Component payload enumeration comprising various log components used by Flow's own int...
Definition: common.hpp:632
@ S_END_SENTINEL
CAUTION – see flow::Flow_log_component doc header for directions to find actual members of this enum ...
Fine_clock::duration Fine_duration
A high-res time duration as computed from two Fine_time_pts.
Definition: common.hpp:410
boost::chrono::high_resolution_clock Fine_clock
Clock used for delicate time measurements, such that the now() method gets the current time relative ...
Definition: common.hpp:404
signed char int8_t
Signed byte. Prefer to use uint8_t when representing binary data. This is 8 bits on all modern system...
Definition: common.hpp:387
Fine_clock::time_point Fine_time_pt
A high-res time point as returned by Fine_clock::now() and suitable for precise time math in general.
Definition: common.hpp:407
const boost::unordered_multimap< Flow_log_component, std::string > S_FLOW_LOG_COMPONENT_NAME_MAP
The map generated by flow::log macro magic that maps each enumerated value in flow::Flow_log_componen...
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