Mapping the File I/O library into the Application
To handle the File I/O library, once again we turn to custom ValueOrError
converters:
// Inject custom ValueOrError conversion
OUTCOME_V2_NAMESPACE_BEGIN
namespace convert
{
// Provide custom ValueOrError conversion from filelib::result<U>
// into any app::outcome<T>
template <class T, class U> //
struct value_or_error<app::outcome<T>, filelib::result<U>>
{
// True 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<filelib::result<U>, std::decay_t<X>>::value //
&& std::is_constructible<T, U>::value>> //
constexpr app::outcome<T> operator()(X &&src)
{
// Forward any successful value
if(src.has_value())
{
return {std::forward<X>(src).value()};
}
// Synthesise a filesystem_error, exactly as if someone had
// called src.value()
auto &fi = src.error();
OUTCOME_V2_NAMESPACE::try_throw_std_exception_from_error(fi.ec); // might throw
return {std::make_exception_ptr( //
filelib::filesystem_error(fi.ec.message(), std::move(fi.path1), std::move(fi.path2), fi.ec))};
}
};
} // namespace convert
OUTCOME_V2_NAMESPACE_END
Note that the conversion exactly duplicates the implementation of
throw_as_system_error_with_payload(failure_info fi)
from
namespace filelib
. In a production implementation, you probably
ought to call that function and catch the exception it throws
into a pointer, as that would be more long term maintainable.