Flow 1.0.2
Flow project: Full implementation reference.
|
#include "flow/error/error_fwd.hpp"
#include "flow/log/log.hpp"
#include "flow/util/detail/util.hpp"
#include <boost/system/system_error.hpp>
#include <stdexcept>
Go to the source code of this file.
Classes | |
class | flow::error::Runtime_error |
An std::runtime_error (which is an std::exception ) that stores an Error_code. More... | |
Namespaces | |
namespace | flow |
Catch-all namespace for the Flow project: A collection of various production-quality modules written in modern C++17, originally by ygoldfel. | |
namespace | flow::error |
Flow module that facilitates working with error codes and exceptions; essentially comprised of niceties on top boost.system's error facility. | |
Macros | |
#define | FLOW_ERROR_EMIT_ERROR(ARG_val) |
Sets *err_code to ARG_val and logs a warning about the error using FLOW_LOG_WARNING(). More... | |
#define | FLOW_ERROR_EMIT_ERROR_LOG_INFO(ARG_val) |
Identical to FLOW_ERROR_EMIT_ERROR(), but the message logged has flow::log::Sev::S_INFO severity instead of S_WARNING . More... | |
#define | FLOW_ERROR_LOG_ERROR(ARG_val) |
Logs a warning about the given error code using FLOW_LOG_WARNING(). More... | |
#define | FLOW_ERROR_SYS_ERROR_LOG_WARNING() FLOW_LOG_WARNING("System error occurred: [" << sys_err_code << "] [" << sys_err_code.message() << "].") |
Logs a warning about the (often errno -based or from a library) error code in sys_err_code . More... | |
#define | FLOW_ERROR_SYS_ERROR_LOG_FATAL() FLOW_LOG_FATAL("System error occurred: [" << sys_err_code << "] [" << sys_err_code.message() << "].") |
Logs a log::Sev::S_FATAL message about the (often errno -based or from a library) error code in sys_err_code , usually just before aborting the process or otherwise entering undefined-behavior land such as via assert(false) . More... | |
#define | FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(ARG_ret_type, ARG_method_name, ...) FLOW_ERROR_EXEC_FUNC_AND_THROW_ON_ERROR(ARG_ret_type, ARG_method_name, this, __VA_ARGS__) |
Narrow-use macro that implements the error code/exception semantics expected of most public-facing Flow (and Flow-inspired) class method APIs. More... | |
#define | FLOW_ERROR_EXEC_FUNC_AND_THROW_ON_ERROR(ARG_ret_type, ARG_function_name, ...) |
Identical to FLOW_ERROR_EXEC_AND_THROW_ON_ERROR() but supports not only class methods but free functions as well. More... | |
Functions | |
template<typename Func , typename Ret > | |
bool | flow::error::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 a preprocessor. More... | |
template<typename Func > | |
bool | flow::error::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. More... | |
#define FLOW_ERROR_EMIT_ERROR | ( | ARG_val | ) |
Sets *err_code
to ARG_val
and logs a warning about the error using FLOW_LOG_WARNING().
An err_code
variable of type that is pointer to flow::Error_code must be declared at the point where the macro is invoked.
ARG_val | Value convertible to flow::Error_code. Reminder: Error_code is trivially/implicitly convertible from any error code set (such as errno s and boost.asio network error code set) that has been boost.system-enabled. |
#define FLOW_ERROR_EMIT_ERROR_LOG_INFO | ( | ARG_val | ) |
Identical to FLOW_ERROR_EMIT_ERROR(), but the message logged has flow::log::Sev::S_INFO severity instead of S_WARNING
.
ARG_val | See FLOW_ERROR_EMIT_ERROR(). |
#define FLOW_ERROR_EXEC_AND_THROW_ON_ERROR | ( | ARG_ret_type, | |
ARG_method_name, | |||
... | |||
) | FLOW_ERROR_EXEC_FUNC_AND_THROW_ON_ERROR(ARG_ret_type, ARG_method_name, this, __VA_ARGS__) |
Narrow-use macro that implements the error code/exception semantics expected of most public-facing Flow (and Flow-inspired) class method APIs.
The semantics it helps implement are explained in flow::Error_code doc header. More formally, here is how to use it:
First, please read flow::Error_code doc header. Next read on:
Suppose you have an API f()
in some class C
that returns type T
and promises, in its doc header, to implement the error reporting semantics listed in the aforementioned flow::Error_code doc header. That is, if user passes in null err_code
, error would cause Run_time error(e_c)
to be thrown; if non-null, then *err_code = e_c
would be set sans exception – e_c
being the error code explaining what went wrong, such as flow::net_flow::error::Code::S_WAIT_INTERRUPTED. Then here's how to use the macro:
A caveat noted in the example above is handling references. If the argument is a const
reference, then to avoid copying it during the intermediate call, surround it with cref()
. We don't tend to use non-const
references (preferring pointers for stylistic reasons), but if you must do so, then use ref()
. All other types are OK to copy (by definition – since you are passing them by value a/k/a copy in the first place in the original signature).
ARG_ret_type | The return type of the invoking method. In practice this would be, for example, size_t when wrapping a receive() (which returns # of bytes received or 0). |
ARG_method_name | Class-qualified name of the invoking method. For example, flow::Node::listen() . Don't forget template parameter values, if applicable; e.g., note the <...> part of Peer_socket::sync_send<Const_buffer_sequence> . |
... | The remaining arguments, of which there must be at least 1, are to simply forward the arguments into the invoking method; thus similar to what one provided when calling the method that must implement the above-discussed error reporting semantics. However, the (mandatory, in this context) Error_code* err_code parameter MUST be replaced by the following identifier: _1 (as for bind() ). |
#define FLOW_ERROR_EXEC_FUNC_AND_THROW_ON_ERROR | ( | ARG_ret_type, | |
ARG_function_name, | |||
... | |||
) |
Identical to FLOW_ERROR_EXEC_AND_THROW_ON_ERROR() but supports not only class methods but free functions as well.
The latter macro is, really, a bit of syntactic sugar on top of the present macro, so that explicitly listing this
as an arg can be omitted in the typical (but not universal) case wherein the API is a class method.
All usage notes for FLOW_ERROR_EXEC_AND_THROW_ON_ERROR() apply here within reason.
This macro exists purely to shorten boiler-plate. Furthermore, we don't like macros (debugger-unfriendly, hard to edit, etc.), so most of it is implemented in a function this macro invokes. However, a function cannot force its caller to return (which we need), so we need the preprocessor at least for that. We also use it to provide halfway useful source code context (file, line #, etc.) info. (It's not THAT useful – it basically identifies the throwing method; not where the method failed; but that's still pretty good and better than what one would achieve without using a macro.)
Another way to have done this would've been to split up T f()
into T f()
and T f_impl()
; where the latter assumes non-null err_code
; while the former calls it and throws flow::error::Runtime_error() if appropriate. That would be less tricky to de-boiler-plate, probably. However, the final code would be longer, and then both functions have to be documented, which is still more boiler-plate. In fact, at that point we might as well have two distinct APIs, as boost.asio does. The way it's done here may be a little trickier, but it's more concise (though, admittedly, a tiny bit slower with one redundant code path that could otherwise be avoided).
The implementation uses bind()
, and a requirement on the ...
macro args is to provide a bind()
-compatible _1
argument. Generally, in this code base, we prefer lambdas over bind()
. In this case, would it be possible to move to a lambda as well? How would the user supply _1
, without bind()
? I am not making this a formal to-do for now, as the existing system already appears slick and not trivially replaceable with lambdas.
ARG_ret_type | The return type of the invoking method. In practice this would be, for example, size_t when wrapping a receive() (which returns # of bytes received or 0). |
ARG_function_name | Class-qualified (if applicable) name of the invoking method. For example, flow::Node::listen() . Don't forget template parameter values, if applicable; e.g., note the <...> part of Peer_socket::sync_send<Const_buffer_sequence> . |
... | The remaining arguments, of which there must be at least 1, are to simply forward the arguments into the invoking method; thus similar to what one provided when calling the method that must implement the above-discussed error reporting semantics. However, the (mandatory, in this context) Error_code* err_code parameter MUST be replaced by the following identifier: _1 (as for bind() ). If ARG_function_name is a class method, then the first arg to ... must be this . However in that case it is more concise and typical to use FLOW_ERROR_EXEC_AND_THROW_ON_ERROR() instead. |
#define FLOW_ERROR_LOG_ERROR | ( | ARG_val | ) |
Logs a warning about the given error code using FLOW_LOG_WARNING().
ARG_val | See FLOW_ERROR_EMIT_ERROR(). |
#define FLOW_ERROR_SYS_ERROR_LOG_FATAL | ( | ) | FLOW_LOG_FATAL("System error occurred: [" << sys_err_code << "] [" << sys_err_code.message() << "].") |
Logs a log::Sev::S_FATAL message about the (often errno
-based or from a library) error code in sys_err_code
, usually just before aborting the process or otherwise entering undefined-behavior land such as via assert(false)
.
sys_err_code
must be an object of type flow::Error_code in the context of the macro's invocation.
This is identical to FLOW_ERROR_SYS_ERROR_LOG_WARNING(), except the message logged has FATAL severity instead of a mere WARNING. Notes in that macro's doc header generally apply. However the use case is different.
The recommended (but in no way enforced or mandatory) pattern is something like:
#define FLOW_ERROR_SYS_ERROR_LOG_WARNING | ( | ) | FLOW_LOG_WARNING("System error occurred: [" << sys_err_code << "] [" << sys_err_code.message() << "].") |
Logs a warning about the (often errno
-based or from a library) error code in sys_err_code
.
sys_err_code
must be an object of type flow::Error_code in the context of the macro's invocation. See also FLOW_ERROR_SYS_ERROR_LOG_FATAL().
Note this implies a convention wherein system (especially errno
-based or from a libary) error codes are to be saved into a stack variable or parameter flow::Error_code sys_err_code
. Of course if you don't like this convention and/or this error message, it is trivial to log something manually.
It's a functional macro despite taking no arguments to convey that it mimics a void
free function.
The recommended (but in no way enforced or mandatory) pattern is something like: