Mapping the HTTP library into the Application 2/2
If you remember the tutorial section on the value_or_error
Concept,
this is an example of how to implement a custom value_or_error
Concept converter
in Outcome:
// Inject custom ValueOrError conversion
OUTCOME_V2_NAMESPACE_BEGIN
namespace convert
{
// Provide custom ValueOrError conversion from
// httplib::result<U> into any app::outcome<T>
template <class T, class U> //
struct value_or_error<app::outcome<T>, httplib::result<U>>
{
// False to indicate that this converter wants `result`/`outcome`
// to NOT reject all other `result`
static constexpr bool enable_result_inputs = true;
// False to indicate that this converter wants `outcome` to NOT
// reject all other `outcome`
static constexpr bool enable_outcome_inputs = true;
template <class X, //
typename = std::enable_if_t<std::is_same<httplib::result<U>, std::decay_t<X>>::value //
&& std::is_constructible<T, U>::value>> //
constexpr app::outcome<T> operator()(X &&src)
{
// Forward any successful value, else synthesise an exception ptr
return src.has_value() ? //
app::outcome<T>{std::forward<X>(src).value()} //
:
app::outcome<T>{app::make_httplib_exception(std::forward<X>(src))};
}
};
} // namespace convert
OUTCOME_V2_NAMESPACE_END
The first thing that you should note is that these custom converters must be injected
directly into the OUTCOME_V2_NAMESPACE::convert
namespace, and they must partially
or completely specialise value_or_error<T, U>
. Here we specialise the
converter for value_or_error
conversions from httplib::result<U>
to app::outcome<T>
i.e. from our third party HTTP library’s error type into our application’s outcome
type (which is unique to our application, as we hard code an app
-local error type).
The second thing to note is that you need to set enable_result_inputs
and enable_outcome_inputs
appropriately, otherwise result
and outcome
inputs will not be matched by this
converter1. In this converter, we really do wish to convert other result
and
outcome
inputs, so we mark these booleans as true
.
The third thing to note is the requirements on operator()
. If the requirements are
not met, the value_or_error
converting constructor in basic_result
and basic_outcome
disables. Note the requirement that the decayed operator()
input X
matches
httplib::result<U>
, and that T
is constructible from U
. This means that the
explicit basic_result(concepts::value_or_error<T, E> &&)
and explicit basic_outcome(concepts::value_or_error<T, E> &&)
constructors are available if, and only if, the input type is a httplib::result<U>
,
and the result’s value type is constructible from the input’s value type.
If operator()
is available, it naturally converts a httplib::result<U>
into an
app::outcome<T>
by either forwarding any success as-is, or calling app::make_httplib_exception()
to type erase the httplib::failure
into an app::httplib_error
.
- Here we refer to
result
andoutcome
as defined by this specific Outcome library. Ifresult
oroutcome
from another Outcome implementation is seen, those always must get parsed via theValueOrError
matching conversion framework. [return]