Flow 1.0.0
Flow project: Full implementation reference.
error.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#include "flow/log/log.hpp"
24#include <boost/system/system_error.hpp>
25#include <stdexcept>
26
27namespace flow::error
28{
29// Types.
30
31/**
32 * An `std::runtime_error` (which is an `std::exception`) that stores an #Error_code. We derive from boost.system's
33 * `system_error` which already does that nicely. This polymorphic subclass merely improves the `what()`
34 * message a little bit -- specially handling the case when there is no #Error_code, or it is falsy -- but is
35 * otherwise identical.
36 *
37 * ### Rationale ###
38 * It is questionable whether Runtime_error is really necessary. One can equally well simply throw
39 * `boost::system::system_error(err_code, context)` when `bool(err_code) == true`, and
40 * `std::runtime_error(context)` otherwise. Indeed the user should feel 100% free to do that if desired.
41 * flow::error::Runtime_error is mere syntactic sugar for code brevity, when one indeed has an `err_code` that may
42 * or may not be falsy: they can just construct+throw a Runtime_error and not worry about the bifurcation.
43 * In the end one likely just catches `std::exception exc` and logs/prints `exc.what()`: how precisely it was thrown
44 * is of low import, typically. Runtime_error provides a concise way (with some arguable niceties like the use
45 * of `String_view`) to throw it when desired.
46 */
49{
50public:
51 // Constructors/destructor.
52
53 /**
54 * Constructs Runtime_error.
55 *
56 * @param err_code_or_success
57 * The #Error_code describing the error if available; or the success value (`Error_code()`)
58 * if an error code is unavailable or inapplicable to this error.
59 * In the latter case what() will omit anything to do with error codes and feature only `context`.
60 * @param context
61 * String describing the context, i.e., where/in what circumstances the error occurred/what happened.
62 * For example: "Peer_socket::receive()" or "Server_socket::accept() while checking
63 * state," or a string with line number and file info. Try to keep it brief but informative.
64 * FLOW_UTIL_WHERE_AM_I_STR() may be helpful.
65 */
66 explicit Runtime_error(const Error_code& err_code_or_success, util::String_view context = "");
67
68 /**
69 * Constructs Runtime_error, when one only has a context string and no applicable/known error code.
70 * Formally it's equivalent to `Runtime_error(Error_code(), context)`: it is syntactic sugar only.
71 *
72 * @param context
73 * See the other ctor.
74 */
75 explicit Runtime_error(util::String_view context);
76
77 // Methods.
78
79 /**
80 * Returns a message describing the exception. Namely:
81 * - If no/success #Error_code passed to ctor: Message includes `context` only.
82 * - If non-success #Error_code passed to ctor: Message includes: numeric value of `Error_code`, a brief
83 * string representing the code category to which it belongs, the system message corresponding to the
84 * `Error_code`, and `context`.
85 *
86 * @return See above.
87 */
88 const char* what() const noexcept override;
89
90private:
91 // Data.
92
93 /**
94 * This is a copy of `context` from ctor if `!err_code_or_success`; or unused otherwise.
95 *
96 * ### Rationale ###
97 * We want what() to act as follows:
98 * - `!err_code_or_success`: Error codes are inapplicable; return `context` only.
99 * - Else: Return a string with `context`; plus all details of the error code.
100 *
101 * The latter occurs in our superclass `what()` already, if we pass up `context` to the super-ctor.
102 * Now suppose `!err_code_or_success`. No matter which super-ctor we use, it will memorize an `Error_code` --
103 * if we pass `Error_code()` it'll remember that; if use a ctor that does not take an `Error_code()` it will
104 * memorize its own `Error_code()`. Therefore we must override `what()` behavior in our own what() in that case.
105 *
106 * Therefore this algorithm works:
107 * - `!err_code_or_success`: Memorize `context` in #m_context_if_no_code. what() just returns the latter.
108 * Do not pass up `context`: we shall never use superclass's `what()` which is the only thing that would
109 * access such a thing.
110 * - Else: Do pass `context` up to super-constructor. Superclass `what()` shall do the right thing.
111 * Do not save to `m_context_if_no_code`: our what() just forwards to superclass `what()`, so the lines
112 * that would ever access #m_context_if_no_code never execute.
113 *
114 * Note: Past versions of boost.system worked differently; it was possible to get by without this.
115 *
116 * ### Performance ###
117 * Using the above algorithm `context` is copied exactly once either way: either into #m_context_if_no_code or
118 * up into superclass's structures. The other guy then just gets a blank string.
119 */
120 const std::string m_context_if_no_code;
121}; // class Runtime_error
122
123// Free functions: in *_fwd.hpp.
124
125// Template implementations.
126
127template<typename Func, typename Ret>
128bool exec_and_throw_on_error(const Func& func, Ret* ret,
129 Error_code* err_code, util::String_view context)
130{
131 /* To really "get" what's happening, just pick an example invoker of the macro which invokes us.
132 * The background is in the FLOW_ERROR_EXEC_AND_THROW_ON_ERROR() doc header; but to really picture the whole thing,
133 * just pick an example and work through how it all fits together.
134 *
135 * Note: This could also be done by simply stuffing all of the below into the macro and eliminating the
136 * present function. Well, we don't roll that way: we like debugger-friendliness, etc., etc.
137 * We use the preprocessor ONLY when what it provides cannot be done without it. */
138
139 if (err_code)
140 {
141 /* All good: the caller can assume non-null err_code; can perform func() equivalent and set *err_code on error.
142 * Our Runtime_error-throwing wrapping services are not required. */
143 return false;
144 }
145
146 /* err_code is null, so we have to make our own Error_code and throw an error if the wrapped operation actually
147 * sets it (indicating an error occurred). */
148 Error_code our_err_code;
149
150 /* Side note: It is assumed (but we don't enforce) that func() is the same as what would execute above in the
151 * `return false;` case. That's why typically the caller (usually an API; say, F(a, b, ..., err_code)) would
152 * use lambda/bind() to maka a functor F(e_c) that executes F(a, b, ..., e_c).
153 * So here we pass our_err_code as "e_c." */
154 *ret = func(&our_err_code);
155
156 if (our_err_code)
157 {
158 /* Error was detected: do our duty. Pass through the context info from caller.
159 * Note that passing in, say, FLOW_UTIL_WHERE_AM_I() would be less useful, since the present location
160 * is not helpful to the log reader in determining where the actual error first occurred. */
161 throw Runtime_error(our_err_code, context);
162 }
163
164 return true;
165} // exec_and_throw_on_error()
166
167template<typename Func>
168bool exec_void_and_throw_on_error(const Func& func, Error_code* err_code, util::String_view context)
169{
170 // See exec_and_throw_on_error(). This is just a simplified version where func() returns void.
171
172 if (err_code)
173 {
174 return false;
175 }
176
177 Error_code our_err_code;
178 func(&our_err_code);
179
180 if (our_err_code)
181 {
182 throw Runtime_error(our_err_code, context);
183 }
184
185 return true;
186} // exec_void_and_throw_on_error()
187
188} // namespace flow::error
189
190// Macros.
191
192/**
193 * Sets `*err_code` to `ARG_val` and logs a warning about the error using FLOW_LOG_WARNING().
194 * An `err_code` variable of type that is pointer to flow::Error_code must be declared at the point where the macro is
195 * invoked.
196 *
197 * @param ARG_val
198 * Value convertible to flow::Error_code. Reminder: `Error_code` is trivially/implicitly convertible from
199 * any error code set (such as `errno`s and boost.asio network error code set) that has been
200 * boost.system-enabled.
201 */
202#define FLOW_ERROR_EMIT_ERROR(ARG_val) \
203 FLOW_UTIL_SEMICOLON_SAFE \
204 ( \
205 ::flow::Error_code FLOW_ERROR_EMIT_ERR_val(ARG_val); \
206 FLOW_LOG_WARNING("Error code emitted: [" << FLOW_ERROR_EMIT_ERR_val << "] " \
207 "[" << FLOW_ERROR_EMIT_ERR_val.message() << "]."); \
208 *err_code = FLOW_ERROR_EMIT_ERR_val; \
209 )
210
211/**
212 * Identical to FLOW_ERROR_EMIT_ERROR(), but the message logged has flow::log::Sev::S_INFO severity instead of
213 * `S_WARNING`.
214 *
215 * @param ARG_val
216 * See FLOW_ERROR_EMIT_ERROR().
217 */
218#define FLOW_ERROR_EMIT_ERROR_LOG_INFO(ARG_val) \
219 FLOW_UTIL_SEMICOLON_SAFE \
220 ( \
221 ::flow::Error_code FLOW_ERROR_EMIT_ERR_LOG_val(ARG_val); \
222 FLOW_LOG_INFO("Error code emitted: [" << FLOW_ERROR_EMIT_ERR_LOG_val << "] " \
223 "[" << FLOW_ERROR_EMIT_ERR_LOG_val.message() << "]."); \
224 *err_code = FLOW_ERROR_EMIT_ERR_LOG_val; \
225 )
226
227/**
228 * Logs a warning about the given error code using FLOW_LOG_WARNING().
229 *
230 * @param ARG_val
231 * See FLOW_ERROR_EMIT_ERROR().
232 */
233#define FLOW_ERROR_LOG_ERROR(ARG_val) \
234 FLOW_UTIL_SEMICOLON_SAFE \
235 ( \
236 ::flow::Error_code FLOW_ERROR_LOG_ERR_val(ARG_val); \
237 FLOW_LOG_WARNING("Error occurred: [" << FLOW_ERROR_LOG_ERR_val << "] " \
238 "[" << FLOW_ERROR_LOG_ERR_val.message() << "]."); \
239 )
240
241/**
242 * Logs a warning about the (often `errno`-based or from a library) error code in `sys_err_code`.
243 * `sys_err_code` must be an object of type flow::Error_code in the context of the macro's invocation.
244 * See also FLOW_ERROR_SYS_ERROR_LOG_FATAL().
245 *
246 * Note this implies a convention wherein system (especially `errno`-based or from a libary) error codes are to
247 * be saved into a stack variable or parameter `flow::Error_code sys_err_code`. Of course if you don't like this
248 * convention and/or this error message, it is trivial to log something manually.
249 *
250 * It's a functional macro despite taking no arguments to convey that it mimics a `void` free function.
251 *
252 * The recommended (but in no way enforced or mandatory) pattern is something like:
253 *
254 * ~~~
255 * Error_code sys_err_code;
256 *
257 * // ...
258 *
259 * // sys_err_code has been set to truthy value. Decision has therefore been made to log about it but otherwise
260 * // recover and continue algorithm.
261 *
262 * FLOW_LOG_WARNING("(...Explain what went wrong; output values of interest; but not `sys_err_code` since....) "
263 * "Details follow.");
264 * FLOW_ERROR_SYS_ERROR_LOG_WARNING();
265 *
266 * // Continue operating. No assert(false); no std::abort()....
267 * ~~~
268 */
269#define FLOW_ERROR_SYS_ERROR_LOG_WARNING() \
270 FLOW_LOG_WARNING("System error occurred: [" << sys_err_code << "] [" << sys_err_code.message() << "].")
271
272/**
273 * Logs a log::Sev::S_FATAL message about the (often `errno`-based or from a library) error code in `sys_err_code`,
274 * usually just before aborting the process or otherwise entering undefined-behavior land such as via `assert(false)`.
275 * `sys_err_code` must be an object of type flow::Error_code in the context of the macro's invocation.
276 *
277 * This is identical to FLOW_ERROR_SYS_ERROR_LOG_WARNING(), except the message logged has FATAL severity instead
278 * of a mere WARNING. Notes in that macro's doc header generally apply. However the use case is different.
279 *
280 * The recommended (but in no way enforced or mandatory) pattern is something like:
281 *
282 * ~~~
283 * Error_code sys_err_code;
284 *
285 * // ...
286 *
287 * // sys_err_code has been set to truthy value that is completely unexpected or so unpalatable as to make
288 * // any attempt at recovery not worth the effort. Decision has therefore been made to abort and/or
289 * // enter undefined-behavior land.
290 *
291 * FLOW_LOG_FATAL("(...Explain what went wrong, and why it is very shocking; output values of interest; but not "
292 * "`sys_err_code` since....) Details follow.");
293 * FLOW_ERROR_SYS_ERROR_LOG_FATAL();
294 *
295 * // Enter undefined-behavior land.
296 * // Different orgs/projects do different things; but a decent approach might be:
297 * assert(false && "(...Re-explain what went wrong, so it shows up in the assert-trip message on some stderr.");
298 * // Possibly really abort program, even if assert()s are disabled via NDEBUG.
299 * std::abort();
300 * ~~~
301 */
302#define FLOW_ERROR_SYS_ERROR_LOG_FATAL() \
303 FLOW_LOG_FATAL("System error occurred: [" << sys_err_code << "] [" << sys_err_code.message() << "].")
304
305/**
306 * Narrow-use macro that implements the error code/exception semantics expected of most public-facing Flow (and
307 * Flow-inspired) class method APIs. The semantics it helps implement are explained in flow::Error_code doc header.
308 * More formally, here is how to use it:
309 *
310 * First, please read flow::Error_code doc header. Next read on:
311 *
312 * Suppose you have an API `f()` in some class `C` that returns type `T` and promises, in its doc header,
313 * to implement the error reporting semantics listed in the aforementioned flow::Error_code doc header. That is, if
314 * user passes in null `err_code`, error would cause `Run_time error(e_c)` to be thrown; if non-null, then
315 * `*err_code = e_c` would be set sans exception -- `e_c` being the error code explaining what went wrong,
316 * such as flow::net_flow::error::Code::S_WAIT_INTERRUPTED. Then here's how to use the macro:
317 *
318 * ~~~
319 * // Example API. In this one, there's a 4th argument that happens to follow the standard Error_code* one.
320 * // arg2 is a (const) reference, as opposed to a pointer or scalar, and is most efficiently handled by adding
321 * // cref() to avoid copying it.
322 * T f(AT1 arg1, const AT2& arg2, Error_code* err_code = 0, AT3 arg3 = 0)
323 * {
324 * FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(T, // Provide the return type of the API.
325 * // Forward all the args into the macro, but replace `err_code` => `_1`.
326 * // Add [c]ref() around any passed-by-reference args.
327 * arg1, cref(arg2), _1, arg3);
328 * // ^-- Call ourselves and return if err_code is null. If got to present line, err_code is not null.
329 *
330 * // ...Bulk of f() goes here! You can now set *err_code to anything without fear....
331 * }
332 * ~~~
333 *
334 * A caveat noted in the example above is handling references. If the argument is a `const` reference, then to avoid
335 * copying it during the intermediate call, surround it with `cref()`. We don't tend to use non-`const` references
336 * (preferring pointers for stylistic reasons), but if you must do so, then use `ref()`. All other types are OK
337 * to copy (by definition -- since you are passing them by value a/k/a copy in the first place in the original
338 * signature).
339 *
340 * @see flow::Error_code for typical error reporting semantics this macro helps implement.
341 * @see exec_void_and_throw_on_error() which you can use directly when ARG_ret_type would be void.
342 * The present macro does not work in that case; but at that point using the function directly is concise enough.
343 *
344 * @param ARG_ret_type
345 * The return type of the invoking method. In practice this would be, for example,
346 * `size_t` when wrapping a `receive()` (which returns # of bytes received or 0).
347 * @param ARG_method_name
348 * Class-qualified name of the invoking method. For example, `flow::Node::listen()`. Don't forget template
349 * parameter values, if applicable; e.g., note the `<...>` part of
350 * `Peer_socket::sync_send<Const_buffer_sequence>`.
351 * @param ...
352 * The remaining arguments, of which there must be at least 1, are to simply forward the arguments into
353 * the invoking method; thus similar to what one provided when calling the method that must implement
354 * the above-discussed error reporting semantics. However, the (mandatory, in this context)
355 * `Error_code* err_code` parameter MUST be replaced by the following identifier: `_1` (as for `bind()`).
356 */
357#define FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(ARG_ret_type, ARG_method_name, ...) \
358 FLOW_ERROR_EXEC_FUNC_AND_THROW_ON_ERROR(ARG_ret_type, ARG_method_name, this, __VA_ARGS__)
359
360/**
361 * Identical to FLOW_ERROR_EXEC_AND_THROW_ON_ERROR() but supports not only class methods but free functions as well.
362 * The latter macro is, really, a bit of syntactic sugar on top of the present macro, so that explicitly listing
363 * `this` as an arg can be omitted in the typical (but not universal) case wherein the API is a class method.
364 *
365 * All usage notes for FLOW_ERROR_EXEC_AND_THROW_ON_ERROR() apply here within reason.
366 *
367 * ### Implementation notes ###
368 * This macro exists purely to shorten boiler-plate. Furthermore, we don't like macros
369 * (debugger-unfriendly, hard to edit, etc.), so most of it is implemented in a function this macro invokes.
370 * However, a function cannot force its caller to return (which we need), so we need the preprocessor at least for that.
371 * We also use it to provide halfway useful source code context (file, line #, etc.) info. (It's not THAT useful --
372 * it basically identifies the throwing method; not where the method failed; but that's still pretty good and better
373 * than what one would achieve without using a macro.)
374 *
375 * Another way to have done this would've been to split up `T f()` into `T f()` and `T f_impl()`; where the latter
376 * assumes non-null `err_code`; while the former calls it and throws flow::error::Runtime_error() if appropriate.
377 * That would be less tricky to de-boiler-plate, probably. However, the final code would be longer, and then both
378 * functions have to be documented, which is still more boiler-plate. In fact, at that point we might as well
379 * have two distinct APIs, as boost.asio does. The way it's done here may be a little trickier, but it's more concise
380 * (though, admittedly, a tiny bit slower with one redundant code path that could otherwise be avoided).
381 *
382 * The implementation uses `bind()`, and a requirement on the `...` macro args is to provide a `bind()`-compatible
383 * `_1` argument. Generally, in this code base, we prefer lambdas over `bind()`. In this case, would it be possible
384 * to move to a lambda as well? How would the user supply `_1`, without `bind()`? I am not making this a formal
385 * to-do for now, as the existing system already appears slick and not trivially replaceable with lambdas.
386 *
387 * @see FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(), particularly all notes on how to use it, as they apply here similarly.
388 *
389 * @param ARG_ret_type
390 * The return type of the invoking method. In practice this would be, for example,
391 * `size_t` when wrapping a `receive()` (which returns # of bytes received or 0).
392 * @param ARG_function_name
393 * Class-qualified (if applicable) name of the invoking method. For example,
394 * `flow::Node::listen()`. Don't forget template parameter values, if applicable; e.g., note the `<...>` part of
395 * `Peer_socket::sync_send<Const_buffer_sequence>`.
396 * @param ...
397 * The remaining arguments, of which there must be at least 1, are to simply forward the arguments into
398 * the invoking method; thus similar to what one provided when calling the method that must implement
399 * the above-discussed error reporting semantics. However, the (mandatory, in this context)
400 * `Error_code* err_code` parameter MUST be replaced by the following identifier: `_1` (as for `bind()`).
401 * If `ARG_function_name` is a class method, then the first arg to `...` must be `this`.
402 * However in that case it is more concise and typical to use FLOW_ERROR_EXEC_AND_THROW_ON_ERROR() instead.
403 */
404#define FLOW_ERROR_EXEC_FUNC_AND_THROW_ON_ERROR(ARG_ret_type, ARG_function_name, ...) \
405 FLOW_UTIL_SEMICOLON_SAFE \
406 ( \
407 using namespace boost::placeholders; /* So they can use _1, _2, etc., without qualification. */ \
408 /* We need both the result of the operation (if applicable) and whether it actually ran. */ \
409 /* So we must introduce this local variable (note it's within a { block } so should minimally interfere with */ \
410 /* the invoker's code). We can't use a sentinel value to combine the two, since the operation's result may */ \
411 /* require ARG_ret_type's entire range. */ \
412 ARG_ret_type result; \
413 /* We provide the function: f(Error_code*), where f(e_c) == this->ARG_method_name(..., e_c, ...). */ \
414 /* Also supply context info of this macro's invocation spot. */ \
415 /* Note that, if f() is executed, it may throw Runtime_error which is the point of its existence. */ \
416 if (::flow::error::exec_and_throw_on_error(::flow::util::bind_ns::bind(&ARG_function_name, __VA_ARGS__), \
417 &result, err_code, \
418 /* See discussion below. */ \
419 FLOW_UTIL_WHERE_AM_I_LITERAL(ARG_function_name))) \
420 { \
421 /* Aforementioned f() WAS executed; did NOT throw (no error); and return value was placed into `result`. */ \
422 return result; \
423 } \
424 /* f() did not run, because err_code is non-null. So no macro invoker should do its thing assuming that fact. */ \
425 /* Recall that the idea is that f() is just recursively calling the method invoking this macro with the same */ \
426 /* arguments except for the Error_code* arg. */ \
427 )
428
429/* Now that we're out of that macro's body with all the backslashes...
430 * Discussion of the FLOW_UTIL_WHERE_AM_I_LITERAL(ARG_function_name) snippet above:
431 *
432 * Considering the potential frequency that FLOW_ERROR_EXEC_FUNC_AND_THROW_ON_ERROR() is invoked in a
433 * an error-reporting API (such as flow::net_flow) -- even *without* an error actually being emitted --
434 * it is important we do not add undue computation. That macro does not take a context string, so it must
435 * compute it itself; as usual we use file/function/line for this. With the technique used above
436 * FLOW_UTIL_WHERE_AM_I_LITERAL() is replaced by a *string literal* -- like:
437 * "/cool/path/to/file.cpp" ":" "Class::someFunc" "(" "332" ")"
438 * which is as compile-time as it gets. So perf-wise that's fantastic; as good as it gets.
439 *
440 * Are there weaknesses? Yes; there is one: Per its doc header FLOW_UTIL_WHERE_AM_I_LITERAL(), due to having
441 * to be replaced by a literal, cannot cut out "/cool/path/to/" from __FILE__, even though in flow.log we do so
442 * for readability of logs. What if we wanted to get rid of this weakness? Then we cannot have a literal there;
443 * we can use other, not-fully-compile-time-computed FLOW_UTIL_WHERE_AM_I*(); that means extra computation
444 * at every call-site. That, too, can be worked-around: One can add an exec_and_throw_on_error() overload
445 * that would take 3 context args instead of 1: strings for file and function, int for line (supplied via
446 * __FILE__, #ARG_function, __LINE__); and it would only actually build a context string to pass to
447 * Runtime_error *if* (1) an error actually occurred; *and* (2) user in fact used err_code=null (meaning throw on
448 * error as opposed to return an Error_code); so lazy-evaluation.
449 *
450 * That would have been a viable approach but:
451 * - still slower (instead of a single compile-time-known pointer, at least 2 ptrs + 1 ints are passed around
452 * the call stack) at each call-site;
453 * - much slower on exception-throwing error (albeit this being relatively rare, typically; not at each call-site);
454 * - much, much more impl code.
455 *
456 * So it's cool; just don't massage __FILE__ in a totally flow.log-consistent way. */
An std::runtime_error (which is an std::exception) that stores an Error_code.
Definition: error.hpp:49
Runtime_error(const Error_code &err_code_or_success, util::String_view context="")
Constructs Runtime_error.
Definition: error.cpp:26
const std::string m_context_if_no_code
This is a copy of context from ctor if !err_code_or_success; or unused otherwise.
Definition: error.hpp:120
const char * what() const noexcept override
Returns a message describing the exception.
Definition: error.cpp:46
Flow module that facilitates working with error codes and exceptions; essentially comprised of niceti...
Definition: error.cpp:22
bool exec_void_and_throw_on_error(const Func &func, Error_code *err_code, util::String_view context)
Equivalent of exec_and_throw_on_error() for operations with void return type.
Definition: error.hpp:168
bool exec_and_throw_on_error(const Func &func, Ret *ret, Error_code *err_code, util::String_view context)
Helper for FLOW_ERROR_EXEC_AND_THROW_ON_ERROR() macro that does everything in the latter not needing ...
Definition: error.hpp:128
Basic_string_view< char > String_view
Commonly used char-based Basic_string_view. See its doc header.
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