Error handling (error_or)

class bad_error_or_access : public std::exception

Thrown by error_or if an object is coerced to a value if it contains an error or if it is coerced to an error if it contains a value.

template<typename T, typename Err = std::error_code>
class error_or

An instance of error_or may contain either an error (of type Err) or a value (of type T).

In addition to methods to query whether an object contains an error or a value, error_or provides an apply operation and a reduce operation. The apply operation can be used to transform an object that contains a value, but retain the error if the object does not contain a value. The reduce operation can be used to transform an error_or into a new value regardless of whether it contained a value or an error.

The template types T and Err must both be complete types that are move-constructible and are neither an array type, reference type, or function type. Err must also be default-constructible and copy-constructible; it is recommended to ensure that Err is trivially copy- and move-constructible. All operations of Err must be noexcept. (This restriction is placed on Err to make all operations on error_or instances with errors uniformly noexcept.)

Example:

using nu::error_or;
using nu::make_error_or;

// default-constructed objects contain Err(), which by default is std::error_code
error_or<int> e;
error_or<int> i(1);

// et will still contain Err()
error_or<int> et = e / [] (int i) { return i+1; };
ASSERT_EQ(et.error(), std::error_code());

// it will contain 2
error_or<int> it = i / [] (int i) { return i+1; };
ASSERT_EQ(it.value(), 2);

// er will be set to -1
int er = e.reduce(
    [] (int i) { return i; },
    [] (std::error_code) { return -1; });
ASSERT_EQ(er, -1);

// ir will be set to 1
int ir = i.reduce(
    [] (int i) { return i; },
    [] (std::error_code) { return -1; });
ASSERT_EQ(ir, 1);

// % can be used to chain operations that may themselves fail
error_or<double> d =
    make_error_or<int>(1)
    / [] (int i) { return i - 1; }
    % [] (int i) -> error_or<double> {
        if (i != 0)
            return i * 3.14;
        else
            return {std::make_error_code(std::errc::invalid_argument)};
    };
ASSERT_FALSE(d);
error_or() noexcept

Constructs the object holding a default-constructed Err as its error().

Noexcept:noexcept
error_or(Err e) noexcept

Constructs the object holding a e as its error(). The value is moved into the newly constructed object.

Noexcept:noexcept
error_or(T value)

Constructs the object holding value as its value(). The provided value is moved into the object.

Noexcept:noexcept if the move constructor of T is noexcept
error_or(error_or &&other)

Constructs the object with the state of other. The state of other is moved into the newly constructed object; other is in an indefinite state afterwards.

Noexcept:noexcept if the function <unspecified> swap(T&, T&) is noexcept, where swap is either std::swap or found by ADL.
error_or &operator=(error_or &&other)

Moves the state of other into *this. Afterwards, other is in an indefinite state.

Noexcept:noexcept if the function <unspecified> swap(T&, T&) is noexcept, where swap is either std::swap or found by ADL.
void swap(error_or &other)

Swaps the states of *this and other.

Noexcept:noexcept if the function <unspecified> swap(T&, T&) is noexcept, where swap is either std::swap or found by ADL.
explicit operator bool() const noexcept
Returns:true if *this contains a value, false otherwise
Noexcept:noexcept
bool operator!() const noexcept
Returns:false if *this contains a value, true otherwise
Noexcept:noexcept
template<typename Fn>
auto operator/(Fn f) const
template<typename Fn>
auto operator/(Fn f)

If *this contains a value, calls f(get_or_release()) and returns the result as an error_or<R> object, where R is the return type of Fn.

These operators are useful because they can provide exception guarantees that the usual sequences if (e) on_value(e.value()); else on_error(e.error()); cannot provide, since both value() and error() may throw.

Returns:*this ? error_or<R>(f(get_or_release())) : error_or<R>(error())
Noexcept:noexcept if f is noexcept
template<typename R = void, typename TrueFn, typename FalseFn>
auto reduce(TrueFn t, FalseFn f) const
template<typename R = void, typename TrueFn, typename FalseFn>
auto reduce(TrueFn t, FalseFn f)

If *this contains a value, calls t(get_or_release()), otherwise calls f(error()). The result of both t(get_or_release()) and f(error()) is converted to type RT and returned, where RT is R if R is not void, otherwise std:common_type<TT, FT>::type where TT and FT are the result types of t(get_or_release()) and f(error()) respectively. The result types of t and f must each be a complete type that is neither an array type, a reference type, or a function type.

These functions are useful because they can provide exception guarantees that the usual sequences if (e) on_value(e.value()); else on_error(e.error()); cannot provide, since both value() and error() may throw.

Returns:*this ? t(get_or_release()) : f(error())
Noexcept:noexcept if t is noexcept and f is noexcept.
template<typename Fn>
auto operator%(Fn f) const
template<typename Fn>
auto operator%(Fn f)

If *this contains a value, calls fn(get_or_release()) and returns the result. If *this does not contain a value, returns error() cast to the result type of fn(get_or_release()). The result type must be a complete type that is neither an array type, a reference type, or a function type.

These operators are provided as a shorthand for reduce(fn1, std::move<Err>), where fn1 would usually be a function that returns another instance of error_or. By using the % operator, multiple calls to functions returning error_or can be easily chained if only the last error in the chain is important, for example if a program must open a file and invoke an ioctl on it in one operation that appears atomic to the consumer.

Returns:*this ? fn(get_or_release()) : error()
Noexcept:noexcept if fn is noexcept.
const Err &error() const
Returns:The error stored in *this.
Throws:nu::bad_error_or_access – If *this contains a value.
const T &value() const &
Returns:The value stored in *this.
Throws:nu::bad_error_or_access – If *this contains an error.
T &&release_value()
Returns:The value stored in *this.
Throws:nu::bad_error_or_access – If *this contains an error.
private const T &get_or_release() const noexcept
private T &&get_or_release() noexcept

Exposition only: if called by const reference, returns a const reference to the stored value. If called by non-const reference, returns an rvalue reference to the stored value.

Free functions for error_or

template<typename T, typename Err>
void swap(error_or<T, Err> &a, error_or<T, Err> &b)

Swap two error_or instances.

Noexcept:noexcept if the function <unspecified> swap(T&, T&) is noexcept, where swap is either std::swap or found by ADL.
template<typename T, typename E = std::error_code, typename ...Args>
error_or<T> make_error_or(Args&&... args)

Creates a new error_or instance with the given arguments as error_or<T, E>(T{std::forward<Args>(args)...}).

Noexcept:noexcept(error_or<T, E>(T{std::forward<Args>(args)...}))