Mapping the HTTP library into the Application <sup>2</sup>⁄<sub>2</sub> - Boost.Outcome documentation

# 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
    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))};
        }
      };
    }  // namespace convert
    BOOST_OUTCOME_V2_NAMESPACE_END

View this code on Github

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 `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`.

1. Here we refer to `result` and `outcome` as defined by this specific Outcome library. If `result` or `outcome` from another Outcome implementation is seen, those always must get parsed via the `ValueOrError` matching conversion framework. [return]

Last revised: December 15, 2020 at 12:22:39 UTC