1
1
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:
Nerijus Arlauskas 2016-07-08 13:32:08 +03:00
parent 89c1bc061b
commit b6e242b36c
9 changed files with 402 additions and 84 deletions

@ -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

@ -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
} }