BRICKS
Small, useful blocks of code, to build bigger things.
Loading...
Searching...
No Matches
bricks::result< T, E > Class Template Reference

A class to represent the result of an operation. More...

#include <result.hpp>

Public Types

using value_type = T
 The value type of the result.
 
using error_type = E
 The error type of the result.
 

Public Member Functions

 result ()=delete
 
 result (const result &) noexcept(std::is_nothrow_copy_constructible_v< variant_t >)=default
 
auto operator= (const result &) noexcept(std::is_nothrow_copy_assignable_v< variant_t >) -> result &=default
 
 result (result &&) noexcept=default
 
auto operator= (result &&) noexcept -> result &=default
 
 ~result ()=default
 
template<typename U , typename std::enable_if_t< std::is_same_v< U, ok< T > >||std::is_same_v< U, err< E > >, bool > = true>
constexpr result (U in) noexcept(std::is_nothrow_move_constructible_v< U >)
 Construct a new result object from a value.
 
template<typename U = T, typename std::enable_if_t<!std::is_same_v< U, E >, bool > = true>
constexpr result (value_type in) noexcept(std::is_nothrow_move_constructible_v< ok< T > >)
 
template<typename U = E, typename std::enable_if_t<!std::is_same_v< T, U >, bool > = true>
constexpr result (error_type in) noexcept(std::is_nothrow_move_constructible_v< err< E > >)
 
template<typename U >
constexpr auto operator= (U in) noexcept -> result &
 Assign a value to the result.
 
constexpr auto is_value () const noexcept -> bool
 Check if the result is a value.
 
constexpr auto is_error () const noexcept -> bool
 Check if the result is an error.
 
constexpr auto expect (const std::string &msg) const -> value_type
 Returns the value of the result.
 
constexpr auto unwrap () const -> value_type
 Returns the value of the result.
 
constexpr auto expect_error (const std::string &msg) const -> error_type
 Returns the error of the result.
 
constexpr auto unwrap_error () const -> error_type
 Returns the error of the result.
 
constexpr auto unwrap_or (value_type default_value) const noexcept -> value_type
 Returns the value of the result.
 
constexpr auto unwrap_or_default () const noexcept -> value_type
 Returns the value of the result or a default constructed value.
 
template<typename F >
constexpr auto unwrap_or_else (F &&f) const -> value_type
 Returns the value of the result or the result of a function.
 
template<typename F >
constexpr auto map (F &&f) const -> result< std::invoke_result_t< F, value_type >, error_type >
 Maps a result<T, E> to result<U, E> by applying a function to a contained value.
 
template<typename F >
constexpr auto map_error (F &&f) const -> result< value_type, std::invoke_result_t< F, error_type > >
 Maps a result<T, E> to result<T, F> by applying a function to a contained error.
 
template<typename F >
constexpr auto map_or (std::invoke_result_t< F, value_type > default_value, F &&f) const -> std::invoke_result_t< F, value_type >
 Returns the provided default (if an error) or applies a function to the contained value.
 
template<typename F , typename G >
constexpr auto map_or_else (G &&default_f, F &&f) const -> std::invoke_result_t< F, value_type >
 Maps the result<T, E> to U by applying fallback function default_f to a contained error, or function f to a contained value.
 
template<typename U >
constexpr auto and_instead (const result< U, error_type > &res) const -> result< U, error_type >
 Returns res if the result is a value, otherwise returns the error.
 
template<typename Op >
constexpr auto and_then (Op &&op) const -> result< typename std::invoke_result_t< Op, value_type >::value_type, error_type >
 Calls op if the result is a value, otherwise returns the error.
 
template<typename F >
constexpr auto or_instead (const result< value_type, F > &res) const -> result< value_type, F >
 Returns res if the result is an error, otherwise returns the value.
 
template<typename Op >
constexpr auto or_else (Op &&op) const -> result< value_type, typename std::invoke_result_t< Op, error_type >::error_type >
 Calls op if the result is an error, otherwise returns the value.
 
constexpr auto operator== (const result &other) const noexcept -> bool
 
constexpr auto operator!= (const result &other) const noexcept -> bool
 

Static Public Member Functions

template<typename F >
static auto from_try_or (F &&f, error_type error_value) noexcept(std::is_nothrow_constructible_v< err< E >, error_type >) -> result
 Construct a new result object from an operation that might throw.
 
template<typename F >
static auto from_try_or_default (F &&f) noexcept(std::is_nothrow_invocable_v< decltype(result< T, E >::from_try_or< F >)> &&std::is_nothrow_constructible_v< error_type >) -> result
 Construct a new result object from an operation that might throw.
 
template<typename F , typename OnError >
static auto from_try_or_else (F &&f, OnError &&on_error) noexcept(std::is_nothrow_invocable_v< OnError > &&std::is_nothrow_constructible_v< result< T, E >, std::invoke_result_t< OnError > >) -> result
 Construct a new result from an operation that might throw or the result of a function.
 

Friends

constexpr auto std::hash (const result &r) const noexcept -> std::size_t
 

Detailed Description

template<typename T, typename E>
class bricks::result< T, E >

A class to represent the result of an operation.

This class is used to represent the result of an operation, which can fail. It can either be a value or an error. It is similar to the std::expected proposal or the rust Result type.

The value type and the error type can be different. If they are the same, one can use the ok<T> and err<E> types to construct and assign the result.

Example:

// Division can fail by either dividing by zero or having a non zero remainder
auto divide = [](int a, int b) -> bricks::result<int, std::invalid_argument> {
if (b == 0) {
return {std::invalid_argument{"division by zero"}};
}
if (a % b != 0) {
return {std::invalid_argument{"non zero remainder"}};
}
return {a / b};
};
// By using the `bricks::result` type, you can avoid throwing exceptions for potential failures
const auto res = divide(42, 2);
if (res.is_value()) {
INFO("The result is ", res.unwrap());
} else {
INFO("The error is: ", res.unwrap_error().what());
}
// It can also be used to easily chain function calls together in a functional style
auto scale_by_two = [](int i) -> bricks::result<int, std::invalid_argument> {
if (i == 0) return {std::invalid_argument{"scaling zero is not supported"}};
return {i * 2};
};
const auto scaled_and_stringified = divide(42, 2)
.and_then(scale_by_two)
.map([](int i) { return std::to_string(i); })
.unwrap_or("something went wrong");
CHECK(scaled_and_stringified == "42");

Member Typedef Documentation

◆ error_type

template<typename T , typename E >
using bricks::result< T, E >::error_type = E

The error type of the result.

◆ value_type

template<typename T , typename E >
using bricks::result< T, E >::value_type = T

The value type of the result.

Constructor & Destructor Documentation

◆ result() [1/6]

template<typename T , typename E >
bricks::result< T, E >::result ( )
delete

◆ result() [2/6]

template<typename T , typename E >
bricks::result< T, E >::result ( const result< T, E > & ) const
defaultnoexcept

◆ result() [3/6]

template<typename T , typename E >
bricks::result< T, E >::result ( result< T, E > && )
defaultnoexcept

◆ ~result()

template<typename T , typename E >
bricks::result< T, E >::~result ( )
default

◆ result() [4/6]

template<typename T , typename E >
template<typename U , typename std::enable_if_t< std::is_same_v< U, ok< T > >||std::is_same_v< U, err< E > >, bool > = true>
bricks::result< T, E >::result ( U in)
inlineconstexprnoexcept

Construct a new result object from a value.

If the value and error types are the same, one must use the ok<T> and err<E> types to construct the result. Otherwise the value type must be convertible to the value type of the result. And the error type must be convertible to the error type of the result.

Example:

// With non equal value and error types you can easily construct a result
// With equal value and error types you have to use the ok and err types
// bricks::result<int, int> res5{42}; // error
Parameters
inThe value to construct the result from.

◆ result() [5/6]

template<typename T , typename E >
template<typename U = T, typename std::enable_if_t<!std::is_same_v< U, E >, bool > = true>
bricks::result< T, E >::result ( value_type in)
inlineconstexprnoexcept

◆ result() [6/6]

template<typename T , typename E >
template<typename U = E, typename std::enable_if_t<!std::is_same_v< T, U >, bool > = true>
bricks::result< T, E >::result ( error_type in)
inlineconstexprnoexcept

Member Function Documentation

◆ and_instead()

template<typename T , typename E >
template<typename U >
auto bricks::result< T, E >::and_instead ( const result< U, error_type > & res) const -> result<U, error_type>
inlinenodiscardconstexpr

Returns res if the result is a value, otherwise returns the error.

This function can be used for control flow based on result values.

Example:

auto parse_int = [](const std::string& s) -> bricks::result<int, std::invalid_argument> {
try {
return {std::stoi(s)};
} catch (const std::invalid_argument& e) {
return {e};
}
};
CHECK(res.and_then(parse_int) // result<int, std::invalid_argument>
.unwrap() == 1);
res = "not a number";
CHECK(res.and_then(parse_int) // result<int, std::invalid_argument>
.is_error());
Parameters
resThe result to return if the result is a value.
Returns
result<U, error_type> The result of the function.

◆ and_then()

template<typename T , typename E >
template<typename Op >
auto bricks::result< T, E >::and_then ( Op && op) const -> result<typename std::invoke_result_t<Op, value_type>::value_type, error_type>
inlinenodiscardconstexpr

Calls op if the result is a value, otherwise returns the error.

This function can be used for control flow based on result values.

Example:

auto divide = [](int a, int b) -> bricks::result<int, std::string> {
if (b == 0) {
return {"division by zero"};
}
return {a / b};
};
auto scale = [](int a) -> bricks::result<int, std::string> {
if (a == 0) {
return {"scaling zero is not allowed"};
}
return {a * 2};
};
CHECK(res.and_then([&](int a) { return divide(a, 2); }) // result<int, std::string>
.and_then(scale) // result<int, std::string>
.unwrap() == 42);
res = 0;
CHECK(res.and_then([&](int a) { return divide(a, 2); }) // result<int, std::string>
.and_then(scale) // result<int, std::string>
.unwrap_error() == "scaling zero is not allowed");
Parameters
opThe operation to perform on the value.
Returns
result<U, error_type>

◆ expect()

template<typename T , typename E >
auto bricks::result< T, E >::expect ( const std::string & msg) const -> value_type
inlinenodiscardconstexpr

Returns the value of the result.

Throws a bad_result_access if the result is an error, with the provided message.

Example:

CHECK_THROWS_WITH_AS([[maybe_unused]] const auto ret = res.expect("The result is an error."),
"The result is an error.", bricks::bad_result_access);
Parameters
msgThe message to use in the exception.
Returns
value_type The value of the result.

◆ expect_error()

template<typename T , typename E >
auto bricks::result< T, E >::expect_error ( const std::string & msg) const -> error_type
inlinenodiscardconstexpr

Returns the error of the result.

Throws a bad_result_access if the result is a value, with the provided message.

Example:

CHECK_THROWS_WITH_AS(
[[maybe_unused]] const auto ret = res.expect_error("The result is a value."),
"The result is a value.", bricks::bad_result_access);
Parameters
msgThe message to use in the exception.
Returns
error_type The error of the result.

◆ from_try_or()

template<typename T , typename E >
template<typename F >
static auto bricks::result< T, E >::from_try_or ( F && f,
error_type error_value ) -> result
inlinestaticnodiscardnoexcept

Construct a new result object from an operation that might throw.

Returns the result of calling function f or the provided error value if the operation throws. This function is useful to convert functions that might throw to functions that return a result.

Example:

// You can use the `from_try_or` function to convert a function that can throw to a function
// that returns a result
auto divide = [](int a, int b) {
if (b == 0) {
throw std::invalid_argument{"division by zero"};
}
return a / b;
};
enum class error { division_by_zero, non_zero_remainder };
const auto res = result<int, error>::from_try_or([divide]() { return divide(42, 2); },
error::division_by_zero);
if (res.is_value()) {
INFO("The result is ", res.unwrap());
} else {
INFO("The error is: ", res.unwrap_error());
}
Parameters
fThe operation to try.
error_valueThe error value to return if the operation throws.
Returns
result<T, E> The result of the operation.

◆ from_try_or_default()

template<typename T , typename E >
template<typename F >
static auto bricks::result< T, E >::from_try_or_default ( F && f) -> result
inlinestaticnodiscardnoexcept

Construct a new result object from an operation that might throw.

Returns the result of calling function f or a default constructed error value if f throws. This function is useful to convert functions that might throw to functions that return a result.

Example:

// You can use the `from_try_or_default` function to convert a function that can throw to a
// function that returns a result
auto divide = [](int a, int b) {
if (b == 0) {
throw std::invalid_argument{"division by zero"};
}
return a / b;
};
const auto res = result<int, float>::from_try_or_default([divide]() { return divide(42, 2); });
if (res.is_value()) {
INFO("The result is ", res.unwrap());
} else {
INFO("The error is: ", res.unwrap_error()); // 0
}
Parameters
fThe operation to try.
Returns
result<T, E> The result of the operation.

◆ from_try_or_else()

template<typename T , typename E >
template<typename F , typename OnError >
static auto bricks::result< T, E >::from_try_or_else ( F && f,
OnError && on_error ) -> result
inlinestaticnodiscardnoexcept

Construct a new result from an operation that might throw or the result of a function.

Returns the result of calling f if it doesn't throw. If the f throws, returns the result of the provided on_error function.

Example:

// You can use the `from_try_or_else` function to convert a function that can throw to a
// function that returns a result
auto divide = [](int a, int b) {
if (b == 0) {
throw std::invalid_argument{"division by zero"};
}
return a / b;
};
enum class error { division };
[divide]() { return divide(42, 2); }, []() noexcept { return error::division; });
if (res.is_value()) {
INFO("The result is ", res.unwrap());
} else {
INFO("The error is: ", res.unwrap_error());
}
Parameters
fThe operation to try.
on_errorThe function to call if f throws.
Returns
result The result of the operation.

◆ is_error()

template<typename T , typename E >
auto bricks::result< T, E >::is_error ( ) const -> bool
inlinenodiscardconstexprnoexcept

Check if the result is an error.

Example:

if (res.is_error()) {
// do something with the error
}
Returns
true If the result is an error.
false If the result is a value.

◆ is_value()

template<typename T , typename E >
auto bricks::result< T, E >::is_value ( ) const -> bool
inlinenodiscardconstexprnoexcept

Check if the result is a value.

Example:

if (res.is_value()) {
// do something with the value
}
Returns
true If the result is a value.
false If the result is an error.

◆ map()

template<typename T , typename E >
template<typename F >
auto bricks::result< T, E >::map ( F && f) const -> result<std::invoke_result_t<F, value_type>, error_type>
inlinenodiscardconstexpr

Maps a result<T, E> to result<U, E> by applying a function to a contained value.

This function can be used to compose the results of two functions.

Example:

auto parse_int = [](const std::string& s) -> bricks::result<int, std::invalid_argument> {
try {
return {std::stoi(s)};
} catch (const std::invalid_argument& e) {
return {e};
}
};
auto to_string = [](int i) { return std::to_string(i); };
CHECK(res.and_then(parse_int).map(to_string).unwrap() == "42");
res = "not a number";
CHECK(res.and_then(parse_int).map(to_string).is_error());
Template Parameters
FThe type of the function to apply.
Parameters
fThe function to apply.
Returns
result<std::invoke_result_t<F, value_type>, error_type> The result of the function.

◆ map_error()

template<typename T , typename E >
template<typename F >
auto bricks::result< T, E >::map_error ( F && f) const -> result<value_type, std::invoke_result_t<F, error_type>>
inlinenodiscardconstexpr

Maps a result<T, E> to result<T, F> by applying a function to a contained error.

This function can be used to pass through a successful result while handling an error.

Example:

auto parse_int = [](const std::string& s) -> bricks::result<int, std::invalid_argument> {
try {
return {std::stoi(s)};
} catch (const std::invalid_argument&) {
return {std::invalid_argument{"invalid argument"}};
}
};
auto error_to_string = [](const std::invalid_argument& e) -> std::string { return e.what(); };
CHECK(res.and_then(parse_int).map_error(error_to_string).unwrap() == 42);
res = "invalid";
CHECK(res.and_then(parse_int).map_error(error_to_string).unwrap_error() == "invalid argument");
Template Parameters
FThe type of the function to apply.
Parameters
fThe function to apply.
Returns
result<value_type, std::invoke_result_t<F, error_type>> The result of the function.

◆ map_or()

template<typename T , typename E >
template<typename F >
auto bricks::result< T, E >::map_or ( std::invoke_result_t< F, value_type > default_value,
F && f ) const -> std::invoke_result_t<F, value_type>
inlinenodiscardconstexpr

Returns the provided default (if an error) or applies a function to the contained value.

Example:

CHECK(res.map_or(0, [](int value) { return value * 2; }) == 84);
res = "error";
CHECK(res.map_or(0, [](int value) { return value * 2; }) == 0);
Parameters
default_valueThe default value to return if the result is an error.
fThe function to apply if the result is a value.
Returns
std::invoke_result_t<F, value_type> The result of the function.

◆ map_or_else()

template<typename T , typename E >
template<typename F , typename G >
auto bricks::result< T, E >::map_or_else ( G && default_f,
F && f ) const -> std::invoke_result_t<F, value_type>
inlinenodiscardconstexpr

Maps the result<T, E> to U by applying fallback function default_f to a contained error, or function f to a contained value.

This function can be used to unpack a successful result while handling an error.

Example:

auto print_and_return = [](const std::string& e) {
INFO(e);
return 0;
};
CHECK(res.map_or_else(print_and_return, [](int value) { return value * 2; }) == 84);
res = "error";
CHECK(res.map_or_else(print_and_return, [](int value) { return value * 2; }) == 0);
Parameters
default_fThe fallback function to apply if the result is an error.
fThe function to apply if the result is a value.
Returns
std::invoke_result_t<F, value_type> The result of the function.

◆ operator!=()

template<typename T , typename E >
auto bricks::result< T, E >::operator!= ( const result< T, E > & other) const -> bool
inlinenodiscardconstexprnoexcept

◆ operator=() [1/3]

template<typename T , typename E >
auto bricks::result< T, E >::operator= ( const result< T, E > & ) -> result &=default
defaultnoexcept

◆ operator=() [2/3]

template<typename T , typename E >
auto bricks::result< T, E >::operator= ( result< T, E > && ) -> result &=default
defaultnoexcept

◆ operator=() [3/3]

template<typename T , typename E >
template<typename U >
auto bricks::result< T, E >::operator= ( U in) -> result&
inlineconstexprnoexcept

Assign a value to the result.

If the value and error types are the same, one must use the ok<T> and err<E> types for the assignment. Otherwise the value type must be convertible to the value type of the result. And the error type must be convertible to the error type of the result.

Example:

CHECK(res.is_error());
res = 42;
CHECK(res.is_value());
res = "error";
CHECK(res.is_error());
res = ok<int>{42};
CHECK(res.is_value());
res = err<std::string>{"error"};
CHECK(res.is_error());

◆ operator==()

template<typename T , typename E >
auto bricks::result< T, E >::operator== ( const result< T, E > & other) const -> bool
inlinenodiscardconstexprnoexcept

◆ or_else()

template<typename T , typename E >
template<typename Op >
auto bricks::result< T, E >::or_else ( Op && op) const -> result<value_type, typename std::invoke_result_t<Op, error_type>::error_type>
inlinenodiscardconstexpr

Calls op if the result is an error, otherwise returns the value.

This function can be used for control flow based on result values.

Example:

auto parse_int = [](const std::string& s) -> result<int, std::invalid_argument> {
try {
return {std::stoi(s)};
} catch (const std::invalid_argument& e) {
return {e};
}
};
auto parse_invalid_argument = [](const std::invalid_argument& e) {
return result<int, std::string>{std::string{"Couldn't parse: "} + e.what()};
};
CHECK(res.and_then(parse_int) // result<int, std::invalid_argument>
.or_else(parse_invalid_argument) // result<int, std::string>
.unwrap() == 42);
res = "not a number";
CHECK(res.and_then(parse_int) // result<int, std::invalid_argument>
.or_else(parse_invalid_argument) // result<int, std::string>
.unwrap_error() == "Couldn't parse: stoi");
Parameters
opThe operation to perform on the error.
Returns
result<value_type, F>

◆ or_instead()

template<typename T , typename E >
template<typename F >
auto bricks::result< T, E >::or_instead ( const result< value_type, F > & res) const -> result<value_type, F>
inlinenodiscardconstexpr

Returns res if the result is an error, otherwise returns the value.

This function can be used for control flow based on result values.

Example:

auto parse_int = [](const std::string& s) -> result<int, std::invalid_argument> {
try {
return {std::stoi(s)};
} catch (const std::invalid_argument& e) {
return {e};
}
};
CHECK(res.and_then(parse_int) // result<int, std::invalid_argument>
.or_instead(result<int, std::string>{"Couldn't parse"})
.unwrap() == 42);
res = "not a number";
CHECK(res.and_then(parse_int) // result<int, std::invalid_argument>
.or_instead(result<int, std::string>{"Couldn't parse"})
.unwrap_error() == "Couldn't parse");
Parameters
resThe result to return if the result is an error.
Returns
result<value_type, F> The result of the function.

◆ unwrap()

template<typename T , typename E >
auto bricks::result< T, E >::unwrap ( ) const -> value_type
inlinenodiscardconstexpr

Returns the value of the result.

Throws a bad_result_access if the result is an error.

Example:

CHECK_THROWS_WITH_AS([[maybe_unused]] const auto ret = res.unwrap(),
"Called `unwrap` on a result that is an error.",
Returns
value_type The value of the result.

◆ unwrap_error()

template<typename T , typename E >
auto bricks::result< T, E >::unwrap_error ( ) const -> error_type
inlinenodiscardconstexpr

Returns the error of the result.

Throws a bad_result_access if the result is a value.

Example:

CHECK_THROWS_WITH_AS([[maybe_unused]] const auto ret = res.unwrap_error(),
"Called `unwrap_error` on a result that is a value.",
Returns
error_type The error of the result.

◆ unwrap_or()

template<typename T , typename E >
auto bricks::result< T, E >::unwrap_or ( value_type default_value) const -> value_type
inlinenodiscardconstexprnoexcept

Returns the value of the result.

If the result is a value, returns the value. If the result is an error, returns the provided default value.

Example:

CHECK(res.unwrap_or(0) == 42);
res = "error";
CHECK(res.unwrap_or(0) == 0);
Parameters
default_valueThe default value to return if the result is an error.
Returns
value_type The value of the result.

◆ unwrap_or_default()

template<typename T , typename E >
auto bricks::result< T, E >::unwrap_or_default ( ) const -> value_type
inlinenodiscardconstexprnoexcept

Returns the value of the result or a default constructed value.

If the result is a value, returns the value. If the result is an error, returns the default constructed value.

Example:

CHECK(res.unwrap_or_default() == 42);
res = "error";
CHECK(res.unwrap_or_default() == 0);
Returns
value_type The value of the result.

◆ unwrap_or_else()

template<typename T , typename E >
template<typename F >
auto bricks::result< T, E >::unwrap_or_else ( F && f) const -> value_type
inlinenodiscardconstexpr

Returns the value of the result or the result of a function.

If the result is a value, returns the value. If the result is an error, returns the result of calling the provided function.

Example:

auto print_and_return = [](const std::string& e) {
INFO(e);
return 0;
};
CHECK(res.unwrap_or_else(print_and_return) == 42);
res = "error";
CHECK(res.unwrap_or_else(print_and_return) == 0);
Parameters
fThe function to call if the result is an error. The function must take a single parameter of type error_type and return a value of type value_type.
Returns
value_type The value of the result.

Friends And Related Symbol Documentation

◆ std::hash

template<typename T , typename E >
auto std::hash ( const result< T, E > & r) const -> std::size_t
friend

The documentation for this class was generated from the following file: