Mapping the HTTP library into the Application 2⁄2
If you remember the tutorial section on the ValueOrError
Concept,
this is an example of how to implement a custom ValueOrError
Concept converter
in Outcome:
// Inject custom ValueOrError conversion
BOOST_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))};
}
};
}
BOOST_OUTCOME_V2_NAMESPACE_END
The first thing that you should note is that these custom converters must be injected
directly into the BOOST_OUTCOME_V2_NAMESPACE::convert
namespace, and they must partially
or completely specialise struct value_or_error<DEST, SRC>
.
The second thing to note is that you need to set enable_result_inputs
and enable_outcome_inputs
appropriately. The reason that these are here is because of a chicken-or-egg problem
whereby the ValueOrError
infrastructure needs to be defined before result
can be defined, and
we also wish to permit inclusion of result
without needing to include outcome
.
So this is a slightly irritating workaround, but one with low impact.
The third thing to note is the SFINAE on operator()
. If the SFINAE fails, the ValueOrError
converting constructor disables. Obviously, if you are using Concept requirements,
that works too. Note the requirement that SFINAEd X
matches httplib::result<U>
, and that
T
is constructible from U
.
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
.