mirror of
https://github.com/trafi/maybe-result-cpp
synced 2024-11-22 02:32:02 +01:00
Move accessors, additional tests and docs.
This commit is contained in:
parent
89c1bc061b
commit
b6e242b36c
@ -13,6 +13,11 @@ AlignAfterOpenBracket: true
|
|||||||
AlwaysBreakAfterDefinitionReturnType: None
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
AlwaysBreakTemplateDeclarations: true
|
AlwaysBreakTemplateDeclarations: true
|
||||||
IndentCaseLabels: true
|
IndentCaseLabels: true
|
||||||
PenaltyReturnTypeOnItsOwnLine: 300
|
PenaltyBreakBeforeFirstCallParameter: 1
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 200
|
||||||
NamespaceIndentation: All
|
NamespaceIndentation: All
|
||||||
Standard: Cpp11
|
Standard: Cpp11
|
68
src/maybe/platforms.hpp
Normal file
68
src/maybe/platforms.hpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Figure out waht features to enable based on compiler versions.
|
||||||
|
*
|
||||||
|
* This logic is re-used from std::experimental::optional.
|
||||||
|
*/
|
||||||
|
|
||||||
|
# if defined __GNUC__ // NOTE: GNUC is also defined for Clang
|
||||||
|
# if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)
|
||||||
|
# define MAYBE_RESULT_GCC_4_8_AND_HIGHER___
|
||||||
|
# elif (__GNUC__ > 4)
|
||||||
|
# define MAYBE_RESULT_GCC_4_8_AND_HIGHER___
|
||||||
|
# endif
|
||||||
|
#
|
||||||
|
# if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)
|
||||||
|
# define MAYBE_RESULT_GCC_4_7_AND_HIGHER___
|
||||||
|
# elif (__GNUC__ > 4)
|
||||||
|
# define MAYBE_RESULT_GCC_4_7_AND_HIGHER___
|
||||||
|
# endif
|
||||||
|
#
|
||||||
|
# if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ >= 1)
|
||||||
|
# define MAYBE_RESULT_GCC_4_8_1_AND_HIGHER___
|
||||||
|
# elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)
|
||||||
|
# define MAYBE_RESULT_GCC_4_8_1_AND_HIGHER___
|
||||||
|
# elif (__GNUC__ > 4)
|
||||||
|
# define MAYBE_RESULT_GCC_4_8_1_AND_HIGHER___
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#
|
||||||
|
# if defined __clang_major__
|
||||||
|
# if (__clang_major__ == 3 && __clang_minor__ >= 5)
|
||||||
|
# define MAYBE_RESULT_CLANG_3_5_AND_HIGHTER_
|
||||||
|
# elif (__clang_major__ > 3)
|
||||||
|
# define MAYBE_RESULT_CLANG_3_5_AND_HIGHTER_
|
||||||
|
# endif
|
||||||
|
# if defined MAYBE_RESULT_CLANG_3_5_AND_HIGHTER_
|
||||||
|
# define MAYBE_RESULT_CLANG_3_4_2_AND_HIGHER_
|
||||||
|
# elif (__clang_major__ == 3 && __clang_minor__ == 4 && __clang_patchlevel__ >= 2)
|
||||||
|
# define MAYBE_RESULT_CLANG_3_4_2_AND_HIGHER_
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if defined __clang__
|
||||||
|
# if (__clang_major__ > 2) || (__clang_major__ == 2) && (__clang_minor__ >= 9)
|
||||||
|
# define MAYBE_RESULT_HAS_THIS_RVALUE_REFS 1
|
||||||
|
# else
|
||||||
|
# define MAYBE_RESULT_HAS_THIS_RVALUE_REFS 0
|
||||||
|
# endif
|
||||||
|
# elif defined MAYBE_RESULT_GCC_4_8_1_AND_HIGHER___
|
||||||
|
# define MAYBE_RESULT_HAS_THIS_RVALUE_REFS 1
|
||||||
|
# else
|
||||||
|
# define MAYBE_RESULT_HAS_THIS_RVALUE_REFS 0
|
||||||
|
# endif
|
||||||
|
|
||||||
|
|
||||||
|
# if defined MAYBE_RESULT_CLANG_3_5_AND_HIGHTER_ && (defined __cplusplus) && (__cplusplus != 201103L)
|
||||||
|
# define MAYBE_RESULT_HAS_MOVE_ACCESSORS 1
|
||||||
|
# else
|
||||||
|
# define MAYBE_RESULT_HAS_MOVE_ACCESSORS 0
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# // In C++11 constexpr implies const, so we need to make non-const members also non-constexpr
|
||||||
|
# if (defined __cplusplus) && (__cplusplus == 201103L)
|
||||||
|
# define MAYBE_RESULT_MUTABLE_CONSTEXPR
|
||||||
|
# else
|
||||||
|
# define MAYBE_RESULT_MUTABLE_CONSTEXPR constexpr
|
||||||
|
# endif
|
@ -10,9 +10,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "platforms.hpp"
|
||||||
#include "result.fwd.hpp"
|
#include "result.fwd.hpp"
|
||||||
|
|
||||||
namespace maybe {
|
namespace maybe {
|
||||||
@ -35,51 +37,77 @@ namespace maybe {
|
|||||||
|
|
||||||
// workaround: std utility functions aren't constexpr yet
|
// workaround: std utility functions aren't constexpr yet
|
||||||
template <class T>
|
template <class T>
|
||||||
inline constexpr T&& constexpr_forward(typename std::remove_reference<T>::type& t) noexcept
|
inline constexpr T&& mr_constexpr_forward(typename std::remove_reference<T>::type& t) noexcept
|
||||||
{
|
{
|
||||||
return static_cast<T&&>(t);
|
return static_cast<T&&>(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
inline constexpr T&& constexpr_forward(typename std::remove_reference<T>::type&& t) noexcept
|
inline constexpr T&& mr_constexpr_forward(typename std::remove_reference<T>::type&& t) noexcept
|
||||||
{
|
{
|
||||||
static_assert(!std::is_lvalue_reference<T>::value, "!!");
|
static_assert(!std::is_lvalue_reference<T>::value, "!!");
|
||||||
return static_cast<T&&>(t);
|
return static_cast<T&&>(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline constexpr typename std::remove_reference<T>::type&& mr_constexpr_move(T&& t) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<typename std::remove_reference<T>::type&&>(t);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename E>
|
template <typename T, typename E>
|
||||||
class result final {
|
class result final {
|
||||||
|
private:
|
||||||
|
constexpr void copy_from(const result<T, E>& other) noexcept;
|
||||||
|
constexpr void set_from(result<T, E>& other) noexcept;
|
||||||
|
constexpr void init_ok(T&& value) noexcept;
|
||||||
|
constexpr void init_ok(const T& value) noexcept;
|
||||||
|
constexpr void init_err(E&& value) noexcept;
|
||||||
|
constexpr void init_err(const E& value) noexcept;
|
||||||
|
constexpr void clear() noexcept;
|
||||||
|
|
||||||
|
internal::Value tag;
|
||||||
|
union {
|
||||||
|
T ok_val;
|
||||||
|
E err_val;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef T ok_type;
|
typedef T ok_type;
|
||||||
|
typedef T err_type;
|
||||||
|
|
||||||
result() : tag(internal::Value::NONE)
|
result() : tag(internal::Value::NONE)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ok initializer */
|
// Internal initialization.
|
||||||
|
|
||||||
|
/** Ok initializer */
|
||||||
result(T&& value, internal::Placeholder) : tag(internal::Value::OK)
|
result(T&& value, internal::Placeholder) : tag(internal::Value::OK)
|
||||||
{
|
{
|
||||||
init_ok(std::forward<T>(value));
|
init_ok(std::forward<T>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ok initializer */
|
/** Ok initializer */
|
||||||
result(const T& value, internal::Placeholder) : tag(internal::Value::OK)
|
result(const T& value, internal::Placeholder) : tag(internal::Value::OK)
|
||||||
{
|
{
|
||||||
init_ok(value);
|
init_ok(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** err initializer */
|
/** Err initializer */
|
||||||
result(internal::Placeholder, E&& value) : tag(internal::Value::ERR)
|
result(internal::Placeholder, E&& value) : tag(internal::Value::ERR)
|
||||||
{
|
{
|
||||||
init_err(std::forward<E>(value));
|
init_err(std::forward<E>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** err initializer */
|
/** Err initializer */
|
||||||
result(internal::Placeholder, const E& value) : tag(internal::Value::ERR)
|
result(internal::Placeholder, const E& value) : tag(internal::Value::ERR)
|
||||||
{
|
{
|
||||||
init_err(value);
|
init_err(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up and tear down.
|
||||||
|
|
||||||
/** copy constructor */
|
/** copy constructor */
|
||||||
result(const result& other) noexcept
|
result(const result& other) noexcept
|
||||||
{
|
{
|
||||||
@ -114,42 +142,196 @@ namespace maybe {
|
|||||||
clear();
|
clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Static construction helpers.
|
||||||
|
|
||||||
/** helper constructor for default ok value */
|
/** helper constructor for default ok value */
|
||||||
constexpr static result<T, E> default_ok() noexcept
|
constexpr static result<T, E> default_ok() noexcept
|
||||||
{
|
{
|
||||||
return result<T, E>(T(), internal::Placeholder{});
|
return mr_constexpr_move(result<T, E>(T(), internal::Placeholder{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** helper constructor for ok value */
|
/** helper constructor for ok value */
|
||||||
constexpr static result<T, E> ok(T&& value) noexcept
|
constexpr static result<T, E> ok(T&& value) noexcept
|
||||||
{
|
{
|
||||||
return result<T, E>(std::forward<T>(value), internal::Placeholder{});
|
return mr_constexpr_move(result<T, E>(std::forward<T>(value), internal::Placeholder{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** helper constructor for ok value */
|
/** helper constructor for ok value */
|
||||||
constexpr static result<T, E> ok(const T& value) noexcept
|
constexpr static result<T, E> ok(const T& value) noexcept
|
||||||
{
|
{
|
||||||
return result<T, E>(value, internal::Placeholder{});
|
return mr_constexpr_move(result<T, E>(value, internal::Placeholder{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** helper constructor for default err value */
|
/** helper constructor for default err value */
|
||||||
constexpr static result<T, E> default_err() noexcept
|
constexpr static result<T, E> default_err() noexcept
|
||||||
{
|
{
|
||||||
return result<T, E>(internal::Placeholder{}, E());
|
return mr_constexpr_move(result<T, E>(internal::Placeholder{}, E()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** helper constructor for err value */
|
/** helper constructor for err value */
|
||||||
constexpr static result<T, E> err(E&& err) noexcept
|
constexpr static result<T, E> err(E&& err) noexcept
|
||||||
{
|
{
|
||||||
return result<T, E>(internal::Placeholder{}, std::forward<E>(err));
|
return mr_constexpr_move(result<T, E>(internal::Placeholder{}, std::forward<E>(err)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** helper constructor for err value */
|
/** helper constructor for err value */
|
||||||
constexpr static result<T, E> err(const E& err) noexcept
|
constexpr static result<T, E> err(const E& err) noexcept
|
||||||
{
|
{
|
||||||
return result<T, E>(internal::Placeholder{}, err);
|
return mr_constexpr_move(result<T, E>(internal::Placeholder{}, err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if MAYBE_RESULT_HAS_MOVE_ACCESSORS == 1
|
||||||
|
|
||||||
|
MAYBE_RESULT_MUTABLE_CONSTEXPR T* operator->()
|
||||||
|
{
|
||||||
|
return ok_dataptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
MAYBE_RESULT_MUTABLE_CONSTEXPR T& operator*() &
|
||||||
|
{
|
||||||
|
return *ok_dataptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
MAYBE_RESULT_MUTABLE_CONSTEXPR T&& operator*() &&
|
||||||
|
{
|
||||||
|
return mr_constexpr_move(*ok_dataptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr T const& ok_value() const&
|
||||||
|
{
|
||||||
|
return is_ok() ? ok_val : (throw bad_result_access("bad ok result access"), ok_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
MAYBE_RESULT_MUTABLE_CONSTEXPR T& ok_value() &
|
||||||
|
{
|
||||||
|
return is_ok() ? ok_val : (throw bad_result_access("bad ok result access"), ok_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
MAYBE_RESULT_MUTABLE_CONSTEXPR T&& ok_value() &&
|
||||||
|
{
|
||||||
|
if (!is_ok())
|
||||||
|
throw bad_result_access("bad ok result access");
|
||||||
|
return std::move(ok_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr E const& err_value() const&
|
||||||
|
{
|
||||||
|
return is_err() ? err_val : (throw bad_result_access("bad err result access"), err_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
MAYBE_RESULT_MUTABLE_CONSTEXPR E& err_value() &
|
||||||
|
{
|
||||||
|
return is_err() ? err_val : (throw bad_result_access("bad err result access"), err_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
MAYBE_RESULT_MUTABLE_CONSTEXPR E&& err_value() &&
|
||||||
|
{
|
||||||
|
if (!is_err())
|
||||||
|
throw bad_result_access("bad err result access");
|
||||||
|
return std::move(err_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
T* operator->()
|
||||||
|
{
|
||||||
|
return ok_dataptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator*()
|
||||||
|
{
|
||||||
|
return *ok_dataptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr T const& ok_value() const
|
||||||
|
{
|
||||||
|
return is_ok() ? ok_val : (throw bad_result_access("bad ok result access"), ok_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
T& ok_value()
|
||||||
|
{
|
||||||
|
return is_ok() ? ok_val : (throw bad_optional_access("bad ok result access"), ok_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr E const& err_value() const
|
||||||
|
{
|
||||||
|
return is_err() ? err_val : (throw bad_result_access("bad err result access"), err_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
E& err_value()
|
||||||
|
{
|
||||||
|
return is_err() ? err_val
|
||||||
|
: (throw bad_optional_access("bad err result access"), err_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MAYBE_RESULT_HAS_THIS_RVALUE_REFS == 1
|
||||||
|
|
||||||
|
template <class V>
|
||||||
|
constexpr T ok_value_or(V&& v) const&
|
||||||
|
{
|
||||||
|
return is_ok() ? ok_val : static_cast<T>(mr_constexpr_forward<V>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class V>
|
||||||
|
constexpr E err_value_or(V&& v) const&
|
||||||
|
{
|
||||||
|
return is_err() ? err_val : static_cast<E>(mr_constexpr_forward<V>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MAYBE_RESULT_HAS_MOVE_ACCESSORS == 1
|
||||||
|
|
||||||
|
template <class V>
|
||||||
|
MAYBE_RESULT_MUTABLE_CONSTEXPR T ok_value_or(V&& v) &&
|
||||||
|
{
|
||||||
|
return is_ok() ? mr_constexpr_move(const_cast<result<T, E>&>(*this).ok_val)
|
||||||
|
: static_cast<T>(mr_constexpr_forward<V>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class V>
|
||||||
|
MAYBE_RESULT_MUTABLE_CONSTEXPR E err_value_or(V&& v) &&
|
||||||
|
{
|
||||||
|
return is_err() ? mr_constexpr_move(const_cast<result<T, E>&>(*this).err_val)
|
||||||
|
: static_cast<E>(mr_constexpr_forward<V>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template <class V>
|
||||||
|
T ok_value_or(V&& v) &&
|
||||||
|
{
|
||||||
|
return is_ok() ? mr_constexpr_move(const_cast<result<T, E>&>(*this).ok_val)
|
||||||
|
: static_cast<T>(mr_constexpr_forward<V>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class V>
|
||||||
|
E err_value_or(V&& v) &&
|
||||||
|
{
|
||||||
|
return is_err() ? mr_constexpr_move(const_cast<result<T, E>&>(*this).err_val)
|
||||||
|
: static_cast<E>(mr_constexpr_forward<V>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template <class V>
|
||||||
|
constexpr T ok_value_or(V&& v) const
|
||||||
|
{
|
||||||
|
return is_ok() ? ok_val : static_cast<T>(mr_constexpr_forward<V>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class V>
|
||||||
|
constexpr E err_value_or(V&& v) const
|
||||||
|
{
|
||||||
|
return is_err() ? err_val : static_cast<T>(mr_constexpr_forward<V>(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Inspection.
|
||||||
|
|
||||||
constexpr bool is_ok() const noexcept
|
constexpr bool is_ok() const noexcept
|
||||||
{
|
{
|
||||||
return tag == internal::Value::OK;
|
return tag == internal::Value::OK;
|
||||||
@ -160,53 +342,27 @@ namespace maybe {
|
|||||||
return tag == internal::Value::ERR;
|
return tag == internal::Value::ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T const& ok_value() const&
|
|
||||||
{
|
|
||||||
return is_ok() ? ok_val : (throw bad_result_access("bad ok result access"), ok_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr T& ok_value() &
|
|
||||||
{
|
|
||||||
return is_ok() ? ok_val : (throw bad_result_access("bad ok result access"), ok_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr E const& err_value() const&
|
|
||||||
{
|
|
||||||
return is_err() ? err_val : (throw bad_result_access("bad err result access"), err_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr E& err_value() &
|
|
||||||
{
|
|
||||||
return is_err() ? err_val : (throw bad_result_access("bad err result access"), err_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class V>
|
|
||||||
constexpr T ok_value_or(V&& v) const&
|
|
||||||
{
|
|
||||||
return is_ok() ? ok_val : static_cast<T>(constexpr_forward<V>(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class V>
|
|
||||||
constexpr E err_value_or(V&& v) const&
|
|
||||||
{
|
|
||||||
return is_err() ? err_val : static_cast<E>(constexpr_forward<V>(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit constexpr operator bool() const noexcept
|
explicit constexpr operator bool() const noexcept
|
||||||
{
|
{
|
||||||
return is_ok();
|
return is_ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unsafe access.
|
||||||
|
|
||||||
constexpr T* ok_dataptr() const noexcept
|
constexpr T* ok_dataptr() const noexcept
|
||||||
{
|
{
|
||||||
|
assert(is_ok());
|
||||||
return std::addressof(ok_val);
|
return std::addressof(ok_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr E* err_dataptr() const noexcept
|
constexpr E* err_dataptr() const noexcept
|
||||||
{
|
{
|
||||||
|
assert(is_err());
|
||||||
return std::addressof(err_val);
|
return std::addressof(err_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Functional helpers.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a result<T, E> to result<U, E> (where U is return value of F(T)) by applying a
|
* Maps a result<T, E> to result<U, E> (where U is return value of F(T)) by applying a
|
||||||
* function F to a
|
* function F to a
|
||||||
@ -220,6 +376,15 @@ namespace maybe {
|
|||||||
template <typename F>
|
template <typename F>
|
||||||
constexpr auto map(F f) noexcept -> maybe::result<typename std::result_of<F(T)>::type, E>;
|
constexpr auto map(F f) noexcept -> maybe::result<typename std::result_of<F(T)>::type, E>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a result<T, E> to result<U, E> by always returning provided U value on success,
|
||||||
|
* leaving an err value untouched.
|
||||||
|
*
|
||||||
|
* This function can be used to compose the results of two functions.
|
||||||
|
*
|
||||||
|
* @param value U
|
||||||
|
* @return maybe::result<U, E>
|
||||||
|
*/
|
||||||
template <typename U>
|
template <typename U>
|
||||||
constexpr auto map_value(U value) noexcept -> maybe::result<U, E>;
|
constexpr auto map_value(U value) noexcept -> maybe::result<U, E>;
|
||||||
|
|
||||||
@ -237,6 +402,18 @@ namespace maybe {
|
|||||||
constexpr auto map_err(F f) noexcept
|
constexpr auto map_err(F f) noexcept
|
||||||
-> maybe::result<T, typename std::result_of<F(E)>::type>;
|
-> maybe::result<T, typename std::result_of<F(E)>::type>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a result<T, E> to result<T, U> by always returning provided U value on error,
|
||||||
|
* leaving an ok value untouched.
|
||||||
|
*
|
||||||
|
* This function can be used to compose the results of two functions.
|
||||||
|
*
|
||||||
|
* @param value U
|
||||||
|
* @return maybe::result<T, U>
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
constexpr auto map_err_value(U value) noexcept -> maybe::result<T, U>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls op if the result is ok, otherwise returns the err value of self.
|
* Calls op if the result is ok, otherwise returns the err value of self.
|
||||||
*
|
*
|
||||||
@ -249,28 +426,7 @@ namespace maybe {
|
|||||||
constexpr auto and_then(F op) noexcept -> typename std::result_of<F(T)>::type;
|
constexpr auto and_then(F op) noexcept -> typename std::result_of<F(T)>::type;
|
||||||
|
|
||||||
template <typename R>
|
template <typename R>
|
||||||
constexpr auto into_err() noexcept -> R
|
constexpr auto into_err() noexcept -> R;
|
||||||
{
|
|
||||||
if (is_err()) {
|
|
||||||
return R::err(std::forward<E>(err_value()));
|
|
||||||
}
|
|
||||||
return R::default_ok();
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
constexpr void copy_from(const result<T, E>& other) noexcept;
|
|
||||||
constexpr void set_from(result<T, E>& other) noexcept;
|
|
||||||
constexpr void init_ok(T&& value) noexcept;
|
|
||||||
constexpr void init_ok(const T& value) noexcept;
|
|
||||||
constexpr void init_err(E&& value) noexcept;
|
|
||||||
constexpr void init_err(const E& value) noexcept;
|
|
||||||
constexpr void clear() noexcept;
|
|
||||||
|
|
||||||
internal::Value tag;
|
|
||||||
union {
|
|
||||||
T ok_val;
|
|
||||||
E err_val;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename E>
|
template <typename T, typename E>
|
||||||
|
@ -133,6 +133,17 @@ constexpr auto maybe::result<T, E>::map_err(F f) noexcept
|
|||||||
return return_result_t::err(f(std::forward<E>(err_value())));
|
return return_result_t::err(f(std::forward<E>(err_value())));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T, typename E>
|
||||||
|
template <typename U>
|
||||||
|
constexpr auto maybe::result<T, E>::map_err_value(U value) noexcept -> maybe::result<T, U>
|
||||||
|
{
|
||||||
|
if (is_ok()) {
|
||||||
|
return maybe::result<T, U>::ok(std::forward<T>(ok_value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return maybe::result<T, U>::err(std::forward<U>(value));
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T, typename E>
|
template <typename T, typename E>
|
||||||
template <typename F>
|
template <typename F>
|
||||||
constexpr auto maybe::result<T, E>::and_then(F f) noexcept -> typename std::result_of<F(T)>::type
|
constexpr auto maybe::result<T, E>::and_then(F f) noexcept -> typename std::result_of<F(T)>::type
|
||||||
@ -143,4 +154,14 @@ constexpr auto maybe::result<T, E>::and_then(F f) noexcept -> typename std::resu
|
|||||||
return maybe::result<typename result_t::ok_type, E>::err(std::forward<E>(err_value()));
|
return maybe::result<typename result_t::ok_type, E>::err(std::forward<E>(err_value()));
|
||||||
}
|
}
|
||||||
return f(std::forward<T>(ok_value()));
|
return f(std::forward<T>(ok_value()));
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename E>
|
||||||
|
template <typename R>
|
||||||
|
constexpr auto maybe::result<T, E>::into_err() noexcept -> R
|
||||||
|
{
|
||||||
|
if (is_err()) {
|
||||||
|
return R::err(std::forward<E>(err_value()));
|
||||||
|
}
|
||||||
|
return R::default_ok();
|
||||||
};
|
};
|
@ -61,11 +61,11 @@ TEST_CASE("example")
|
|||||||
auto success_result = run(true);
|
auto success_result = run(true);
|
||||||
|
|
||||||
REQUIRE(success_result);
|
REQUIRE(success_result);
|
||||||
REQUIRE(success_result.ok_value() == 4);
|
REQUIRE(4 == success_result.ok_value());
|
||||||
|
|
||||||
auto error_result = run(false);
|
auto error_result = run(false);
|
||||||
|
|
||||||
REQUIRE(!error_result);
|
REQUIRE(!error_result);
|
||||||
REQUIRE(error_result.err_value() == "file not found");
|
REQUIRE("file not found" == error_result.err_value());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -36,6 +36,6 @@ TEST_CASE("result_and_then")
|
|||||||
auto a = result<A, int>::err(43);
|
auto a = result<A, int>::err(43);
|
||||||
auto b = a.and_then([](A v) { return result<B, int>::ok(B(v.value + " world")); });
|
auto b = a.and_then([](A v) { return result<B, int>::ok(B(v.value + " world")); });
|
||||||
REQUIRE(!b);
|
REQUIRE(!b);
|
||||||
REQUIRE(b.err_value() == 43);
|
REQUIRE(43 == b.err_value());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,6 +10,14 @@ public:
|
|||||||
std::string value;
|
std::string value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class B final {
|
||||||
|
public:
|
||||||
|
B(std::string value) : value(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
std::string value;
|
||||||
|
};
|
||||||
|
|
||||||
using maybe::result;
|
using maybe::result;
|
||||||
|
|
||||||
TEST_CASE("result_map_err")
|
TEST_CASE("result_map_err")
|
||||||
@ -19,7 +27,7 @@ TEST_CASE("result_map_err")
|
|||||||
auto a = result<A, bool>::err(true);
|
auto a = result<A, bool>::err(true);
|
||||||
auto b = a.map_err([](bool v) { return v ? 42 : 43; });
|
auto b = a.map_err([](bool v) { return v ? 42 : 43; });
|
||||||
REQUIRE(!b);
|
REQUIRE(!b);
|
||||||
REQUIRE(b.err_value() == 42);
|
REQUIRE(42 == b.err_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("does not convert anything and returns ok if not err")
|
SECTION("does not convert anything and returns ok if not err")
|
||||||
@ -29,4 +37,20 @@ TEST_CASE("result_map_err")
|
|||||||
REQUIRE(b);
|
REQUIRE(b);
|
||||||
REQUIRE(b.ok_value().value == "hi");
|
REQUIRE(b.ok_value().value == "hi");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("replaces err A with err B")
|
||||||
|
{
|
||||||
|
auto a = result<int, A>::err(A("hello"));
|
||||||
|
auto b = a.map_err_value(B("bye"));
|
||||||
|
REQUIRE(!b);
|
||||||
|
REQUIRE(b.err_value().value == "bye");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("does not replace err A with err B if A is ok")
|
||||||
|
{
|
||||||
|
auto a = result<int, A>::ok(43);
|
||||||
|
auto b = a.map_err_value(B("bye"));
|
||||||
|
REQUIRE(b);
|
||||||
|
REQUIRE(43 == b.ok_value());
|
||||||
|
}
|
||||||
}
|
}
|
@ -35,6 +35,22 @@ TEST_CASE("result_map")
|
|||||||
auto a = result<A, int>::err(43);
|
auto a = result<A, int>::err(43);
|
||||||
auto b = a.map([](A v) { return B(v.value = " world"); });
|
auto b = a.map([](A v) { return B(v.value = " world"); });
|
||||||
REQUIRE(!b);
|
REQUIRE(!b);
|
||||||
REQUIRE(b.err_value() == 43);
|
REQUIRE(43 == b.err_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("replaces result A with result B")
|
||||||
|
{
|
||||||
|
auto a = result<A, int>::ok(A("hello"));
|
||||||
|
auto b = a.map_value(B("bye"));
|
||||||
|
REQUIRE(b);
|
||||||
|
REQUIRE(b.ok_value().value == "bye");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("does not replace result A with result B if A is err")
|
||||||
|
{
|
||||||
|
auto a = result<A, int>::err(43);
|
||||||
|
auto b = a.map_value(B("bye"));
|
||||||
|
REQUIRE(!b);
|
||||||
|
REQUIRE(43 == b.err_value());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +1,6 @@
|
|||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <maybe/result.hpp>
|
#include <maybe/result.hpp>
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
using maybe::result;
|
using maybe::result;
|
||||||
|
|
||||||
@ -46,7 +43,7 @@ TEST_CASE("result")
|
|||||||
REQUIRE(res.is_ok());
|
REQUIRE(res.is_ok());
|
||||||
REQUIRE(!res.is_err());
|
REQUIRE(!res.is_err());
|
||||||
REQUIRE(res);
|
REQUIRE(res);
|
||||||
REQUIRE(res.ok_value() == 12);
|
REQUIRE(12 == res.ok_value());
|
||||||
|
|
||||||
auto other_ok = result<int, int>::ok(12);
|
auto other_ok = result<int, int>::ok(12);
|
||||||
REQUIRE(res == other_ok);
|
REQUIRE(res == other_ok);
|
||||||
@ -61,7 +58,7 @@ TEST_CASE("result")
|
|||||||
REQUIRE(res.is_err());
|
REQUIRE(res.is_err());
|
||||||
REQUIRE(!res.is_ok());
|
REQUIRE(!res.is_ok());
|
||||||
REQUIRE(!res);
|
REQUIRE(!res);
|
||||||
REQUIRE(res.err_value() == 12);
|
REQUIRE(12 == res.err_value());
|
||||||
|
|
||||||
auto other_err = result<int, int>::err(12);
|
auto other_err = result<int, int>::err(12);
|
||||||
REQUIRE(res == other_err);
|
REQUIRE(res == other_err);
|
||||||
@ -77,13 +74,13 @@ TEST_CASE("result")
|
|||||||
REQUIRE(ok != err);
|
REQUIRE(ok != err);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("throws exception if invalid value accessed")
|
SECTION("throws exception if invalid value accessed")
|
||||||
{
|
{
|
||||||
auto ok = result<int, int>::ok(12);
|
auto ok = result<int, int>::ok(12);
|
||||||
auto err = result<int, int>::err(12);
|
auto err = result<int, int>::err(12);
|
||||||
REQUIRE_THROWS(ok.err_value());
|
REQUIRE_THROWS(ok.err_value());
|
||||||
REQUIRE_THROWS(err.ok_value());
|
REQUIRE_THROWS(err.ok_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("returns default values")
|
SECTION("returns default values")
|
||||||
{
|
{
|
||||||
@ -194,4 +191,35 @@ TEST_CASE("result")
|
|||||||
|
|
||||||
REQUIRE(ss.str() == "[err]");
|
REQUIRE(ss.str() == "[err]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if MAYBE_RESULT_HAS_MOVE_ACCESSORS == 1
|
||||||
|
|
||||||
|
SECTION("ok move accessor works")
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto val = result<NoCopy, NoCopy>::ok(NoCopy(42, [&] { ss << "[ok]"; }));
|
||||||
|
auto&& other = val.ok_value();
|
||||||
|
REQUIRE(42 == other.get_flag());
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(ss.str() == "[ok]");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("err move accessor works")
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto val = result<NoCopy, NoCopy>::err(NoCopy(42, [&] { ss << "[err]"; }));
|
||||||
|
auto&& other = val.err_value();
|
||||||
|
REQUIRE(42 == other.get_flag());
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(ss.str() == "[err]");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user