3.7.0
Freundlich's C++ toolkit
Classes | Typedefs | Functions
fcppt.either

Description

An optional with an error type.

Motivation

The downside of exceptions, which are the usual way to handle errors in C++, is that they do not appear in the type system. If a function changes which exceptions it throws, all the code that uses the function will still compile. This can make keeping track of which errors to handle where extremely difficult. Instead of having a function that throws an exception, i.e.

int example_function()
{
// some code
// ...
if (error())
{
throw std::runtime_error{"something went wrong"};
}
return 1;
}

we can move the exception into the return type instead, like so:

int_or_exception example_function2()
{
// some code
// ...
if (error())
{
return int_or_exception{std::runtime_error{"something went wrong"}};
}
return int_or_exception{1};
}

In the first example, where the exception is used, a caller has to know that such an exception may be thrown and catch it.

void caller()
{
try
{
int const result{example_function()};
std::cout << "result is " << result << '\n';
}
catch (std::runtime_error const &)
{
// do something else
}
}

In the second example, where the either is used, we cannot ignore that an error can occur. The simplest way to deconstruct an either<F,S> is to call the fcppt::either::match function. It gets two lambdas, where the first one receives F and the second one receives S.

void caller2()
{
example_function2(),
[](std::runtime_error const &) {
// do something else
},
[](int const _result) { std::cout << "result is " << _result << '\n'; });
}

In general, an either<F,S> is basically the same as a std::variant<F,S> but with additional meaning assigned to F and S. This becomes apparant in functions like fcppt::either::map, fcppt::either::apply and fcppt::either::bind.

Note
Either does not make exceptions obsolete. They are still crucial to implement RAII, and there might be cases where it is simply not desired to make an error explicit in the type system.

Operations

The simplest operation on eithers is fcppt::either::map. It allows us to transform the success value while keeping the error value. For example, consider a function that might return an int, which we want to transform into a string. This can be done as follows:

{
return fcppt::either::map(get_int(), [](int const _value) { return std::to_string(_value); });
}

We often have to chain multiple eithers together. For example, if we want to call two functions, each of which can fail, we only want to produce a result if both succeeed. This can be done with fcppt::either::apply.

{
[](int const _value1, int const _value2) {
return std::to_string(_value1) + " " + std::to_string(_value2);
},
get_int(),
get_int());
}

Note that since fcppt::either::apply is a variadic template, so it can work on arbitrarly many eithers, the function is its first parameter.

Another, even more basic operation, is fcppt::either::bind. As an example, let us look at how reading from a std::istream would work using eithers. Consider a function that tries to read an int and if that succeeds tries to read a string.

using int_string_pair = std::pair<int, std::string>;
int_string_pair read_stream_raw(std::istream &_stream)
{
int result_int{0};
if (!(_stream >> result_int))
{
throw std::runtime_error{"error"};
}
std::string result_string{};
if (!(_stream >> result_string))
{
throw std::runtime_error{"error"};
}
return int_string_pair{result_int, result_string};
}

First note that the function reading from the stream, which is operator>> , uses a reference parameter to store its result. Since operator>> returns a reference to _stream, the expression !(_stream >> something) checks if reading something produced an error. If that is the case, we cannot use the stream to read more data, and we need to abort early.

To do this with eithers instead, first, let us encapsulate reading a single value in a template function which returns an either.

template <typename Type>
{
Type result{};
return _stream >> result ? return_type{result} : return_type{std::runtime_error{"error"}};
}

Then, we can use this function to read the two eithers in succession, which is done using fcppt::either::bind.

{
return fcppt::either::bind(read<int>(_stream), [&_stream](int const _int_value) {
read<std::string>(_stream), [_int_value](std::string const &_string_value) {
return std::make_pair(_int_value, _string_value);
});
});
}

Note that the difference to using fcppt::either::apply is that the second operation is not done if the first one returned an error. Unfortunately, chaining more of these operations together creates more and more nested code. In other programming languages like Haskell, there is special notation to bring every operation on the same indentation level, called the Do-Notation.

If at some point you want to turn an either back into an exception, you can use fcppt::either::to_exception.

int_string_pair either_to_exception(std::istream &_stream)
{
read_stream_either(_stream), [](std::runtime_error const &_error) { return _error; });
}

Errors

When functions return optional errors but no success values, it can be more convenient to handle such cases as eithers. An fcppt::either::error is an either with a success type that only has one value, which is fcppt::unit. For example:

auto either_error(fcppt::optional::object<error_code> const _error)
{
return fcppt::either::map(either_error, [](fcppt::either::no_error) {
// Do something in case of no error.
return do_something();
});
}

Header files

Header file Description
object_fwd.hpp Contains fcppt::either::object either's declaration.
object_decl.hpp Contains fcppt::either::object either's definition.
object_impl.hpp Contains the definition of fcppt::either::object either's member functions.
object.hpp Includes object_impl.hpp and comparison.hpp.
apply.hpp Contains fcppt::either::apply.
bind.hpp Contains fcppt::either::bind.
comparison.hpp Contains operator== and operator!=.
error_fwd.hpp Contains fcppt::either::error error's declaration.
error.hpp Contains fcppt::either::error error's definition.
error_from_optional.hpp Contains fcppt::either::error_from_optional.
failure_opt.hpp Contains fcppt::either::failure_opt.
first_success.hpp Contains fcppt::either::first_success.
from_optional.hpp Contains fcppt::either::from_optional.
make_failure.hpp Contains fcppt::either::make_failure.
make_success.hpp Contains fcppt::either::make_success.
map.hpp Contains fcppt::either::map.
map_failure.hpp Contains fcppt::either::map_failure.
match.hpp Contains fcppt::either::match.
no_error_fwd.hpp Contains fcppt::either::no_error no_error's declaration.
no_error.hpp Contains fcppt::either::no_error no_error's definition.
output.hpp Contains operator<<.
sequence.hpp Contains fcppt::either::sequence.
sequence_error.hpp Contains fcppt::either::sequence_error.
success_opt.hpp Contains fcppt::either::success_opt.
to_exception.hpp Contains fcppt::either::to_exception.
try_call.hpp Contains fcppt::either::try_call.

Classes

class  fcppt::either::object< Failure, Success >
 

Typedefs

template<typename Failure >
using fcppt::either::error = fcppt::either::object< Failure, fcppt::either::no_error >
 An either without a success value. More...
 
template<typename Either >
using fcppt::either::failure_type = typename fcppt::either::detail::failure_type< Either >::type
 The failure type of an either. More...
 
using fcppt::either::no_error = fcppt::unit
 The type that represents no error in an fcppt::either::error. More...
 
template<typename Either >
using fcppt::either::success_type = typename fcppt::either::detail::success_type< Either >::type
 The success type of an either. More...
 

Functions

template<typename Function , typename Either1 , typename... Eithers>
auto fcppt::either::apply (Function const &_function, Either1 &&_either1, Eithers &&..._eithers) -> fcppt::either::object< fcppt::either::failure_type< fcppt::type_traits::remove_cv_ref_t< Either1 >>, decltype(_function(fcppt::move_if_rvalue< Either1 >(_either1.get_success_unsafe()), fcppt::move_if_rvalue< Eithers >(_eithers.get_success_unsafe())...))>
 Applies a function to several eithers. More...
 
template<typename Either , typename Function >
auto fcppt::either::bind (Either &&_either, Function const &_function) -> decltype(_function(fcppt::move_if_rvalue< Either >(_either.get_success_unsafe())))
 Monadic bind on the success type of an either. More...
 
template<typename Failure , typename Success >
bool fcppt::either::operator== (fcppt::either::object< Failure, Success > const &_a, fcppt::either::object< Failure, Success > const &_b)
 Compares two eithers for equality. More...
 
template<typename Failure , typename Success >
bool fcppt::either::operator!= (fcppt::either::object< Failure, Success > const &_a, fcppt::either::object< Failure, Success > const &_b)
 Compares two eithers for inequality. More...
 
template<typename Failure , typename Success >
fcppt::either::object< std::result_of_t< Failure()>, std::result_of_t< Success()> > fcppt::either::construct (bool const _value, Success const &_success, Failure const &_failure)
 Constructs an either by calling one of two functions. More...
 
template<typename Optional >
fcppt::either::error< fcppt::optional::value_type< fcppt::type_traits::remove_cv_ref_t< Optional > > > fcppt::either::error_from_optional (Optional &&_optional)
 Converts an optional error to an either. More...
 
template<typename Either >
fcppt::optional::object< fcppt::either::failure_type< fcppt::type_traits::remove_cv_ref_t< Either > > > fcppt::either::failure_opt (Either &&_either)
 Returns the failure type as an optional. More...
 
template<typename Functions >
fcppt::either::object< std::vector< fcppt::either::failure_type< std::result_of_t< fcppt::type_traits::value_type< Functions >)> > >, fcppt::either::success_type< std::result_of_t< fcppt::type_traits::value_type< Functions >)> > > fcppt::either::first_success (Functions const &_functions)
 Call a container of functions, returning their first success or a container of failures. More...
 
template<typename Optional , typename FailureFunction >
auto fcppt::either::from_optional (Optional &&_optional, FailureFunction const &_failure_function) -> fcppt::either::object< decltype(_failure_function()), fcppt::optional::value_type< fcppt::type_traits::remove_cv_ref_t< Optional >>>
 Converts an optional to an either. More...
 
template<typename Either >
fcppt::either::object< fcppt::either::failure_type< fcppt::type_traits::remove_cv_ref_t< Either > >, fcppt::either::success_type< fcppt::either::success_type< fcppt::type_traits::remove_cv_ref_t< Either > > > > fcppt::either::join (Either &&_either)
 Joins an either. More...
 
template<typename Next , typename Loop >
fcppt::either::failure_type< std::result_of_t< Next()> > fcppt::either::loop (Next const &_next, Loop const &_loop)
 Loops a function returning an either. More...
 
template<typename Success , typename Failure >
fcppt::either::object< fcppt::type_traits::remove_cv_ref_t< Failure >, Success > fcppt::either::make_failure (Failure &&_failure)
 Create an either with a failure. More...
 
template<typename Failure , typename Success >
fcppt::either::object< Failure, fcppt::type_traits::remove_cv_ref_t< Success > > fcppt::either::make_success (Success &&_success)
 Create an either with a success. More...
 
template<typename Either , typename Function >
auto fcppt::either::map (Either &&_either, Function const &_function) -> fcppt::either::object< fcppt::either::failure_type< fcppt::type_traits::remove_cv_ref_t< Either >>, decltype(_function(fcppt::move_if_rvalue< Either >(_either.get_success_unsafe())))>
 Maps over the success type of an either. More...
 
template<typename Either , typename Function >
auto fcppt::either::map_failure (Either &&_either, Function const &_function) -> fcppt::either::object< decltype(_function(fcppt::move_if_rvalue< Either >(_either.get_failure_unsafe()))), fcppt::either::success_type< fcppt::type_traits::remove_cv_ref_t< Either >>>
 Maps over the failure type of an either. More...
 
template<typename Either , typename FailureFunction , typename SuccessFunction >
auto fcppt::either::match (Either &&_either, FailureFunction const &_failure_function, SuccessFunction const &_success_function) -> decltype(_success_function(fcppt::move_if_rvalue< Either >(_either.get_success_unsafe())))
 Matches on the two cases of an either. More...
 
template<typename ResultContainer , typename Source >
fcppt::either::object< fcppt::either::failure_type< fcppt::type_traits::value_type< fcppt::type_traits::remove_cv_ref_t< Source > > >, ResultContainer > fcppt::either::sequence (Source &&_source)
 Sequences a container of eithers. More...
 
template<typename Sequence , typename Function >
auto fcppt::either::sequence_error (Sequence &&_sequence, Function const &_function) -> decltype(_function(std::declval< fcppt::container::to_value_type< std::remove_reference_t< Sequence >>>()))
 Folds over a range, breaking out on the first error. More...
 
template<typename Either >
fcppt::optional::object< fcppt::either::success_type< fcppt::type_traits::remove_cv_ref_t< Either > > > fcppt::either::success_opt (Either &&_either)
 Returns the success type as an optional. More...
 
template<typename Either , typename MakeException >
auto fcppt::either::to_exception (Either &&_either, MakeException const _make_exception) -> decltype(fcppt::move_if_rvalue< Either >(_either.get_success_unsafe()))
 Returns the success value contained in an either or throws an exception. More...
 
template<typename Exception , typename Function , typename ToException >
FCPPT_PP_PUSH_WARNING auto fcppt::either::try_call (Function const &_function, ToException const &_to_exception) -> fcppt::either::object< decltype(_to_exception(std::declval< Exception >())), decltype(_function())>
 Catches exceptions of a function call and puts the result in an either. More...
 

Typedef Documentation

◆ error

template<typename Failure >
using fcppt::either::error = typedef fcppt::either::object<Failure, fcppt::either::no_error>

An either without a success value.

An either with a failure but no success value. This is useful in case errors are returned as optionals.

◆ failure_type

template<typename Either >
using fcppt::either::failure_type = typedef typename fcppt::either::detail::failure_type<Either>::type

The failure type of an either.

Template Parameters
EitherMust be an fcppt::either::object.

◆ no_error

The type that represents no error in an fcppt::either::error.

◆ success_type

template<typename Either >
using fcppt::either::success_type = typedef typename fcppt::either::detail::success_type<Either>::type

The success type of an either.

Template Parameters
EitherMust be an fcppt::either::object.

Function Documentation

◆ apply()

template<typename Function , typename Either1 , typename... Eithers>
auto fcppt::either::apply ( Function const &  _function,
Either1 &&  _either1,
Eithers &&...  _eithers 
) -> fcppt::either::object< fcppt::either::failure_type<fcppt::type_traits::remove_cv_ref_t<Either1>>, decltype(_function( fcppt::move_if_rvalue<Either1>(_either1.get_success_unsafe()), fcppt::move_if_rvalue<Eithers>(_eithers.get_success_unsafe())...))>

Applies a function to several eithers.

For eithers e_1 = _either1 and e_2, ..., e_n = _eithers, let i be the smallest number such that e_i is set to failure f. If such an i exists, f is returned. Otherwise, e_1, ...., e_n are set to successes s_1,...,s_n and the result is _function(s_1,...,s_n).

◆ bind()

template<typename Either , typename Function >
auto fcppt::either::bind ( Either &&  _either,
Function const &  _function 
) -> decltype(_function(fcppt::move_if_rvalue<Either>(_either.get_success_unsafe())))

Monadic bind on the success type of an either.

If _either is set to success s, then _function(s) is returned. Otherwise, the failure in _either is returned.

Template Parameters
FunctionA function callable as either<Either::failure,R> (Either::success) where R is the result type

◆ construct()

template<typename Failure , typename Success >
fcppt::either::object<std::result_of_t<Failure()>, std::result_of_t<Success()> > fcppt::either::construct ( bool const  _value,
Success const &  _success,
Failure const &  _failure 
)

Constructs an either by calling one of two functions.

If _value is true then _success() is returned. Otherwise, _failure() is returned.

◆ error_from_optional()

template<typename Optional >
fcppt::either::error<fcppt::optional::value_type<fcppt::type_traits::remove_cv_ref_t<Optional> > > fcppt::either::error_from_optional ( Optional &&  _optional)

Converts an optional error to an either.

If _optional is set to x, then x is returned as the failure value.

◆ failure_opt()

template<typename Either >
fcppt::optional::object<fcppt::either::failure_type<fcppt::type_traits::remove_cv_ref_t<Either> > > fcppt::either::failure_opt ( Either &&  _either)

Returns the failure type as an optional.

◆ first_success()

template<typename Functions >
fcppt::either::object< std::vector< fcppt::either::failure_type<std::result_of_t<fcppt::type_traits::value_type<Functions>)> > >, fcppt::either::success_type<std::result_of_t<fcppt::type_traits::value_type<Functions>)> > > fcppt::either::first_success ( Functions const &  _functions)

Call a container of functions, returning their first success or a container of failures.

For _functions = (f_1,...,f_n), let i be the smallest index such that f_i() returns success s, in which case the result is s. If there is no such index, then all functions return failures e_1,...,e_n and the result is (e_1,...,_e_n).

Template Parameters
FunctionsA container of functions callable as fcppt::either::object<F,S> ()

◆ from_optional()

template<typename Optional , typename FailureFunction >
auto fcppt::either::from_optional ( Optional &&  _optional,
FailureFunction const &  _failure_function 
) -> fcppt::either::object< decltype(_failure_function()), fcppt::optional::value_type<fcppt::type_traits::remove_cv_ref_t<Optional>>>

Converts an optional to an either.

If _optional is set to x, then x is returned as the success value, otherwise _failure_function() is returned as the failure value.

Template Parameters
FailureFunctionMust be a function callable as R () where R is the failure type

◆ join()

Joins an either.

If the outer either has failure f_1, then f_1 is returned. Otherwise, if the inner either has failure f_2, then f_2 is returned. Otherwise, the success value of the inner either is returned.

Template Parameters
EitherMust be an fcppt::either::object<F,fcppt::either::object<F,S>>.

◆ loop()

template<typename Next , typename Loop >
fcppt::either::failure_type<std::result_of_t<Next()> > fcppt::either::loop ( Next const &  _next,
Loop const &  _loop 
)

Loops a function returning an either.

Calls _next repeatedly until it returns a failure, which is then returned as the result. Each success value that is returned until then is passed to _loop.

Template Parameters
NextA function callable as R (), where R is an either.
LoopA Function callable as void (success_type<R>), where R is the result of Next.

◆ make_failure()

template<typename Success , typename Failure >
fcppt::either::object<fcppt::type_traits::remove_cv_ref_t<Failure>, Success> fcppt::either::make_failure ( Failure &&  _failure)
inline

Create an either with a failure.

Template Parameters
SuccessThe success type of the either.

◆ make_success()

template<typename Failure , typename Success >
fcppt::either::object<Failure, fcppt::type_traits::remove_cv_ref_t<Success> > fcppt::either::make_success ( Success &&  _success)
inline

Create an either with a success.

Template Parameters
FailureThe failure type of the either.

◆ map()

template<typename Either , typename Function >
auto fcppt::either::map ( Either &&  _either,
Function const &  _function 
) -> fcppt::either::object< fcppt::either::failure_type<fcppt::type_traits::remove_cv_ref_t<Either>>, decltype(_function(fcppt::move_if_rvalue<Either>(_either.get_success_unsafe())))>

Maps over the success type of an either.

If _either is set to success s, r = _function(s) is called and the result is either<Either::failure,decltype(r)>(r). Otherwise, the failure in _either is returned.

Template Parameters
FunctionA function callable as R (Either::success) where R is the result type

◆ map_failure()

template<typename Either , typename Function >
auto fcppt::either::map_failure ( Either &&  _either,
Function const &  _function 
) -> fcppt::either::object< decltype(_function(fcppt::move_if_rvalue<Either>(_either.get_failure_unsafe()))), fcppt::either::success_type<fcppt::type_traits::remove_cv_ref_t<Either>>>

Maps over the failure type of an either.

If _either is set to failure f, r = _function(f) is called and the result is either<decltype(r),Either::success>(r). Otherwise, the success in _either is returned.

Template Parameters
FunctionA function callable as R (Either::failure) where R is the result type

◆ match()

template<typename Either , typename FailureFunction , typename SuccessFunction >
auto fcppt::either::match ( Either &&  _either,
FailureFunction const &  _failure_function,
SuccessFunction const &  _success_function 
) -> decltype(_success_function(fcppt::move_if_rvalue<Either>(_either.get_success_unsafe())))

Matches on the two cases of an either.

If _either is set to success s, then _success_function(s) is returned. Otherwise, _either is set to failure f and _failure_function(f) is returned.

Template Parameters
FailureFunctionA function callable as R (Either::failure) where R is the result type
SuccessFunctionA function callable as R (Either::success) where R is the result type

◆ operator!=()

template<typename Failure , typename Success >
bool fcppt::either::operator!= ( fcppt::either::object< Failure, Success > const &  _a,
fcppt::either::object< Failure, Success > const &  _b 
)

Compares two eithers for inequality.

◆ operator==()

template<typename Failure , typename Success >
bool fcppt::either::operator== ( fcppt::either::object< Failure, Success > const &  _a,
fcppt::either::object< Failure, Success > const &  _b 
)

Compares two eithers for equality.

◆ sequence()

template<typename ResultContainer , typename Source >
fcppt::either::object< fcppt::either::failure_type< fcppt::type_traits::value_type<fcppt::type_traits::remove_cv_ref_t<Source> > >, ResultContainer> fcppt::either::sequence ( Source &&  _source)

Sequences a container of eithers.

Let _source be a container [e_1,...e_n] of type fcppt::either::object<F,S>. If there is an i such that e_i has failure f and there is no j < i such that e_j has a failure, then f is returned. Otherwise, all eithers have success values, [s_1,...,s_n], and fcppt::either::object<F,ResultContainer>{s_1,...,s_n} is returned.

Template Parameters
ResultContainerMust be a container of type S
SourceMust be an either type

◆ sequence_error()

template<typename Sequence , typename Function >
auto fcppt::either::sequence_error ( Sequence &&  _sequence,
Function const &  _function 
) -> decltype( _function(std::declval<fcppt::container::to_value_type<std::remove_reference_t<Sequence>>>()))

Folds over a range, breaking out on the first error.

This function is similar to fcppt::either::sequence, except it does not produce an output sequence. Instead, _function returns fcppt::either::error, which on success contains an fcppt::unit on success. Let _sequence = [x_1, ..., x_n]. The algorithms calls _function(x_1), ..., _function(x_i), where _function(x_i) is either the first call that returns a failure, in which case the failure is returned as the result, or i=n, in which case success is returned.

Template Parameters
SequenceMust be a range.
FunctionMust be a function callable as fcppt::either::error<T>(Sequence::value_type) for some type T.

◆ success_opt()

template<typename Either >
fcppt::optional::object<fcppt::either::success_type<fcppt::type_traits::remove_cv_ref_t<Either> > > fcppt::either::success_opt ( Either &&  _either)

Returns the success type as an optional.

◆ to_exception()

template<typename Either , typename MakeException >
auto fcppt::either::to_exception ( Either &&  _either,
MakeException const  _make_exception 
) -> decltype(fcppt::move_if_rvalue<Either>(_either.get_success_unsafe()))
inline

Returns the success value contained in an either or throws an exception.

If _either is set to success s, then s is returned. Otherwise, _either is set to failure f and the result of _make_exception(f) is thrown as an exception.

Template Parameters
MakeExceptionMust be a function callable as E (Either::failure) where E is the exception type

◆ try_call()

template<typename Exception , typename Function , typename ToException >
FCPPT_PP_PUSH_WARNING auto fcppt::either::try_call ( Function const &  _function,
ToException const &  _to_exception 
) -> fcppt::either:: object<decltype(_to_exception(std::declval<Exception>())), decltype(_function())>

Catches exceptions of a function call and puts the result in an either.

Calls _function in a try block. If _function returns s, then the result is success s. If the function throws an exception e of type Exception, then the result is the failure _to_exception(e).

Template Parameters
FunctionA function callable as S () where S is the success result type
ToExceptionA function callable as E (Exception) where E is the failure result type