1
1
mirror of https://github.com/trafi/maybe-result-cpp synced 2024-11-22 02:32:02 +01:00

Use two optional values as backing storage.

This commit is contained in:
Nerijus Arlauskas 2016-07-10 01:37:56 +03:00
parent cd71dd9252
commit 7ee87d5df5
8 changed files with 195 additions and 478 deletions

@ -1,8 +1,10 @@
# Maybe Result
[![Build Status](https://travis-ci.org/trafi/maybe-result-cpp.svg?branch=master)](https://travis-ci.org/trafi/maybe-result-cpp)
Maybe Result is a return value wrapper that can contain either a value
`T` or error `E`. It borrows ideas heavily from the [C++17's
`std::experimental::optional`][optional], [Rust's std::result](result) and
`std::experimental::optional`][optional], [Rust's std::result][result] and
the [`std::expected`][expected] that was proposed but not yet accepted
for C++17.
@ -99,17 +101,32 @@ so that the include would be:
A C++ compiler shat supports C++14 is required.
You can use `-std=c++14` flag for sufficiently recent versions of
`GCC` or `CLANG`.
`GCC` (4.9) or `CLANG` (3.7).
__Warning! Library is highly experimental and is not guaranteed to work.__
## Running tests
Library requires `std::experimental::optional` implementation, location
of which can be specified with `-DEXPERIMENTAL_OPTIONAL_INCLUDE` flag:
```bash
cmake .
cmake -DEXPERIMENTAL_OPTIONAL_INCLUDE=../path/to/optional .
make tests && ./tests/tests
```
There is a script that does this automatically:
```bash
./dev/run-tests.sh
```
In addition to this, you can run tests on all supported compilers using docker:
```bash
./dev/docker-run-tests.sh
```
## License
Licensed under either of

20
dev/docker-run-tests.sh Executable file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
COMMAND="./dev/run-tests.sh"
compilers=( "clang-3.7" "clang-3.8" "gcc-4.9" "gcc-5.2" "gcc-6.1" )
for compiler in "${compilers[@]}"
do
echo "Env ${compiler}"
docker run -v `pwd`:/opt -w=/opt nercury/cmake-cpp:${compiler} /bin/bash -c "${COMMAND}"
if [ $? -ne 0 ]; then
echo "Tests failed on ${compiler}"
exit 1
fi
done
echo "OK!"

14
dev/run-tests.sh Executable file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
SRC=`pwd`
rm -rf /tmp/build
mkdir -p /tmp/build
cd /tmp/build
if [ ! -d "optional" ]; then
mkdir -p optional
curl -Ls https://api.github.com/repos/akrzemi1/Optional/tarball | tar --strip-components=1 -xz -C optional
fi
cmake -DEXPERIMENTAL_OPTIONAL_INCLUDE=/tmp/build/optional ${SRC}
make -j8 tests && ./tests/tests

@ -2,6 +2,9 @@ set(TARGET "maybe_result")
add_library(${TARGET} INTERFACE)
SET(EXPERIMENTAL_OPTIONAL_INCLUDE "" CACHE STRING "Path to experimental optional include directory.")
target_include_directories(${TARGET}
INTERFACE .
INTERFACE ${EXPERIMENTAL_OPTIONAL_INCLUDE}
)

@ -1,68 +0,0 @@
#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,45 +10,17 @@
#pragma once
#include <cassert>
#include <stdexcept>
#include <string>
#include "platforms.hpp"
#include "result.fwd.hpp"
#include <optional.hpp>
#include <string>
namespace maybe {
namespace internal {
enum class Value { OK, ERR, NONE };
struct Placeholder {
struct placeholder {
};
}
class bad_result_access : public std::logic_error {
public:
explicit bad_result_access(const std::string& what_arg) : logic_error{what_arg}
{
}
explicit bad_result_access(const char* what_arg) : logic_error{what_arg}
{
}
};
// workaround: std utility functions aren't constexpr yet
template <class T>
inline constexpr T&& mr_constexpr_forward(typename std::remove_reference<T>::type& t) noexcept
{
return static_cast<T&&>(t);
}
template <class T>
inline constexpr T&& mr_constexpr_forward(typename std::remove_reference<T>::type&& t) noexcept
{
static_assert(!std::is_lvalue_reference<T>::value, "!!");
return static_cast<T&&>(t);
}
template <class T>
inline constexpr typename std::remove_reference<T>::type&& mr_constexpr_move(T&& t) noexcept
{
@ -58,309 +30,150 @@ namespace maybe {
template <typename T, typename E>
class result final {
private:
void copy_from(const result<T, E>& other) noexcept;
void set_from(result<T, E>& other) noexcept;
void init_ok(T&& value) noexcept;
void init_ok(const T& value) noexcept;
void init_err(E&& value) noexcept;
void init_err(const E& value) noexcept;
void clear() noexcept;
internal::Value tag;
union {
T ok_val;
E err_val;
};
std::experimental::optional<T> var_ok;
std::experimental::optional<E> var_err;
public:
typedef T ok_type;
typedef T err_type;
typedef E err_type;
result() : tag(internal::Value::NONE)
result()
{
}
// Internal initialization.
/** Ok initializer */
result(T&& value, internal::Placeholder) : tag(internal::Value::OK)
result(T&& value, internal::placeholder) : var_ok(std::forward<T>(value))
{
init_ok(std::forward<T>(value));
}
/** Ok initializer */
result(const T& value, internal::Placeholder) : tag(internal::Value::OK)
result(internal::placeholder, E&& value) : var_err(std::forward<E>(value))
{
init_ok(value);
}
/** Err initializer */
result(internal::Placeholder, E&& value) : tag(internal::Value::ERR)
{
init_err(std::forward<E>(value));
}
/** Err initializer */
result(internal::Placeholder, const E& value) : tag(internal::Value::ERR)
{
init_err(value);
}
// Set up and tear down.
/** copy constructor */
result(const result& other) noexcept
{
copy_from(other);
}
/** copy assignment */
result& operator=(const result& other) noexcept
{
clear();
copy_from(other);
return *this;
}
/** move constructor */
result(result&& other) noexcept
{
set_from(other);
}
/** move assignment */
result& operator=(result&& other) noexcept
{
clear();
set_from(other);
return *this;
}
/** destructor */
~result<T, E>()
{
clear();
};
// Static construction helpers.
/** helper constructor for default ok value */
constexpr static result<T, E> default_ok() noexcept
{
return mr_constexpr_move(result<T, E>(T(), internal::Placeholder{}));
}
/** helper constructor for ok value */
constexpr static result<T, E> ok(T&& value) noexcept
{
return mr_constexpr_move(result<T, E>(std::forward<T>(value), internal::Placeholder{}));
return std::forward<result<T, E>>(
result<T, E>(std::forward<T>(value), internal::placeholder{}));
}
/** helper constructor for ok value */
constexpr static result<T, E> ok(const T& value) noexcept
constexpr static result<T, E> err(E&& value) noexcept
{
return mr_constexpr_move(result<T, E>(value, internal::Placeholder{}));
return std::forward<result<T, E>>(
result<T, E>(internal::placeholder{}, std::forward<E>(value)));
}
/** helper constructor for default err value */
constexpr static result<T, E> default_err() noexcept
{
return mr_constexpr_move(result<T, E>(internal::Placeholder{}, E()));
}
/** helper constructor for err value */
constexpr static result<T, E> err(E&& err) noexcept
{
return mr_constexpr_move(result<T, E>(internal::Placeholder{}, std::forward<E>(err)));
}
/** helper constructor for err value */
constexpr static result<T, E> err(const E& err) noexcept
{
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_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);
}
E& err_value()
{
return is_err() ? err_val
: (throw bad_result_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
inline bool is_ok() const noexcept
{
return tag == internal::Value::OK;
return !!var_ok;
}
constexpr bool is_err() const noexcept
inline bool is_err() const noexcept
{
return tag == internal::Value::ERR;
return !!var_err;
}
explicit constexpr operator bool() const noexcept
explicit inline operator bool() const noexcept
{
return is_ok();
}
// Unsafe access.
#if OPTIONAL_HAS_MOVE_ACCESSORS == 1
constexpr T* ok_dataptr() noexcept
constexpr T const& ok_value() const&
{
assert(is_ok());
return const_cast<T*>(std::addressof(ok_val));
return var_ok.value();
}
constexpr E* err_dataptr() noexcept
OPTIONAL_MUTABLE_CONSTEXPR T& ok_value() &
{
assert(is_err());
return const_cast<E*>(std::addressof(err_val));
return var_ok.value();
}
OPTIONAL_MUTABLE_CONSTEXPR T&& ok_value() &&
{
return std::move(var_ok.value());
}
#else
T const& ok_value() const
{
return var_ok.value();
}
T& ok_value()
{
return var_ok.value();
}
#endif
#if OPTIONAL_HAS_THIS_RVALUE_REFS == 1
template <class V>
constexpr T ok_value_or(V&& v) const&
{
return var_ok.value_or(std::forward<V>(v));
}
template <class V>
OPTIONAL_MUTABLE_CONSTEXPR T ok_value_or(V&& v) &&
{
return var_err.value_or(std::forward<V>(v));
}
#endif
#if OPTIONAL_HAS_MOVE_ACCESSORS == 1
constexpr E const& err_value() const&
{
return var_err.value();
}
OPTIONAL_MUTABLE_CONSTEXPR E& err_value() &
{
return var_err.value();
}
OPTIONAL_MUTABLE_CONSTEXPR E&& err_value() &&
{
return std::move(var_err.value());
}
#else
E const& err_value() const
{
return var_err.value();
}
E& err_value()
{
return var_err.value();
}
#endif
#if OPTIONAL_HAS_THIS_RVALUE_REFS == 1
template <class V>
constexpr E err_value_or(V&& v) const&
{
return var_err.value_or(std::forward<V>(v));
}
template <class V>
OPTIONAL_MUTABLE_CONSTEXPR E err_value_or(V&& v) &&
{
return var_err.value_or(std::forward<V>(v));
}
#endif
// Functional helpers.
/**
@ -374,7 +187,7 @@ namespace maybe {
* @return maybe::result<U, E>
*/
template <typename F>
auto map(F f) noexcept -> maybe::result<typename std::result_of<F(T)>::type, E>;
inline 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,
@ -386,7 +199,7 @@ namespace maybe {
* @return maybe::result<U, E>
*/
template <typename U>
auto map_value(U value) noexcept -> maybe::result<U, E>;
inline auto map_value(U value) noexcept -> maybe::result<U, E>;
/**
* Maps a result<T, E> to result<T, U> (where U is return value of F(E)) by applying a
@ -400,8 +213,7 @@ namespace maybe {
* @return maybe::result<T, U>
*/
template <typename F>
auto map_err(F f) noexcept
-> maybe::result<T, typename std::result_of<F(E)>::type>;
inline auto map_err(F f) noexcept -> 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,
@ -413,7 +225,7 @@ namespace maybe {
* @return maybe::result<T, U>
*/
template <typename U>
auto map_err_value(U value) noexcept -> maybe::result<T, U>;
inline 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.
@ -424,18 +236,18 @@ namespace maybe {
* @return maybe::result<U, E>
*/
template <typename F>
auto and_then(F op) noexcept -> typename std::result_of<F(T)>::type;
inline auto and_then(F op) noexcept -> typename std::result_of<F(T)>::type;
template <typename R>
auto into_err() noexcept -> R;
inline auto into_err() noexcept -> R;
};
template <typename T, typename E>
constexpr bool operator==(const result<T, E>& x, const result<T, E>& y)
{
return x.is_ok() && y.is_ok()
? *x.ok_dataptr() == *y.ok_dataptr()
: (x.is_err() && y.is_err() ? *x.err_dataptr() == *y.err_dataptr() : false);
? x.ok_value() == y.ok_value()
: (x.is_err() && y.is_err() ? x.err_value() == y.err_value() : false);
}
template <typename T, typename E>

@ -12,94 +12,9 @@
#include "result.hpp"
template <typename T, typename E>
void maybe::result<T, E>::copy_from(const result<T, E>& other) noexcept
{
using ::maybe::internal::Value;
switch (other.tag) {
case Value::OK:
init_ok(other.ok_val);
break;
case Value::ERR:
init_err(other.err_val);
break;
default:
tag = Value::NONE;
break;
}
}
template <typename T, typename E>
void maybe::result<T, E>::set_from(result<T, E>& other) noexcept
{
using ::maybe::internal::Value;
switch (other.tag) {
case Value::OK:
init_ok(std::move(other.ok_val));
break;
case Value::ERR:
init_err(std::move(other.err_val));
break;
default:
tag = Value::NONE;
return;
}
other.tag = Value::NONE;
}
template <typename T, typename E>
void maybe::result<T, E>::init_ok(T&& value) noexcept
{
tag = ::maybe::internal::Value::OK;
::new (static_cast<void*>(ok_dataptr())) T(std::forward<T>(value));
}
template <typename T, typename E>
void maybe::result<T, E>::init_ok(const T& value) noexcept
{
tag = ::maybe::internal::Value::OK;
::new (static_cast<void*>(ok_dataptr())) T(value);
}
template <typename T, typename E>
void maybe::result<T, E>::init_err(E&& value) noexcept
{
tag = ::maybe::internal::Value::ERR;
::new (static_cast<void*>(err_dataptr())) E(std::forward<E>(value));
}
template <typename T, typename E>
void maybe::result<T, E>::init_err(const E& value) noexcept
{
tag = ::maybe::internal::Value::ERR;
::new (static_cast<void*>(err_dataptr())) E(value);
}
template <typename T, typename E>
void maybe::result<T, E>::clear() noexcept
{
using ::maybe::internal::Value;
switch (tag) {
case Value::OK:
(&this->ok_val)->T::~T();
tag = Value::NONE;
break;
case Value::ERR:
(&this->err_val)->E::~E();
tag = Value::NONE;
break;
default:
break;
}
}
template <typename T, typename E>
template <typename F>
auto maybe::result<T, E>::map(F f) noexcept
-> maybe::result<typename std::result_of<F(T)>::type, E>
inline auto maybe::result<T, E>::map(F f) noexcept -> maybe::result<typename std::result_of<F(T)>::type, E>
{
typedef maybe::result<typename std::result_of<F(T)>::type, E> return_result_t;
@ -111,7 +26,7 @@ auto maybe::result<T, E>::map(F f) noexcept
template <typename T, typename E>
template <typename U>
auto maybe::result<T, E>::map_value(U value) noexcept -> maybe::result<U, E>
inline auto maybe::result<T, E>::map_value(U value) noexcept -> maybe::result<U, E>
{
if (is_err()) {
return maybe::result<U, E>::err(std::forward<E>(err_value()));
@ -122,7 +37,7 @@ auto maybe::result<T, E>::map_value(U value) noexcept -> maybe::result<U, E>
template <typename T, typename E>
template <typename F>
auto maybe::result<T, E>::map_err(F f) noexcept
inline auto maybe::result<T, E>::map_err(F f) noexcept
-> maybe::result<T, typename std::result_of<F(E)>::type>
{
typedef maybe::result<T, typename std::result_of<F(E)>::type> return_result_t;
@ -135,7 +50,7 @@ auto maybe::result<T, E>::map_err(F f) noexcept
template <typename T, typename E>
template <typename U>
auto maybe::result<T, E>::map_err_value(U value) noexcept -> maybe::result<T, U>
inline 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()));
@ -146,7 +61,7 @@ auto maybe::result<T, E>::map_err_value(U value) noexcept -> maybe::result<T, U>
template <typename T, typename E>
template <typename F>
auto maybe::result<T, E>::and_then(F f) noexcept -> typename std::result_of<F(T)>::type
inline auto maybe::result<T, E>::and_then(F f) noexcept -> typename std::result_of<F(T)>::type
{
typedef typename std::result_of<F(T)>::type result_t;
@ -158,7 +73,7 @@ auto maybe::result<T, E>::and_then(F f) noexcept -> typename std::result_of<F(T)
template <typename T, typename E>
template <typename R>
auto maybe::result<T, E>::into_err() noexcept -> R
inline auto maybe::result<T, E>::into_err() noexcept -> R
{
if (is_err()) {
return R::err(std::forward<E>(err_value()));

@ -12,7 +12,11 @@ public:
destructor_fun = std::move(other.destructor_fun);
other.destructor_fun = nullptr;
}
NoCopy& operator=(NoCopy&& other) = default;
NoCopy& operator=(NoCopy&& other) {
std::swap(flag, other.flag);
std::swap(destructor_fun, other.destructor_fun);
return *this;
}
NoCopy(int flag, std::function<void()> destructor_fun)
: flag(flag), destructor_fun(destructor_fun)
@ -45,11 +49,11 @@ TEST_CASE("result")
REQUIRE(res);
REQUIRE(12 == res.ok_value());
// auto other_ok = result<int, int>::ok(12);
// REQUIRE(res == other_ok);
//
// auto other_different_ok = result<int, int>::ok(42);
// REQUIRE(res != other_different_ok);
auto other_ok = result<int, int>::ok(12);
REQUIRE(res == other_ok);
auto other_different_ok = result<int, int>::ok(42);
REQUIRE(res != other_different_ok);
}
SECTION("created err result returns err value")
@ -60,18 +64,18 @@ TEST_CASE("result")
REQUIRE(!res);
REQUIRE(12 == res.err_value());
// auto other_err = result<int, int>::err(12);
// REQUIRE(res == other_err);
//
// auto other_different_err = result<int, int>::err(42);
// REQUIRE(res != other_different_err);
auto other_err = result<int, int>::err(12);
REQUIRE(res == other_err);
auto other_different_err = result<int, int>::err(42);
REQUIRE(res != other_different_err);
}
SECTION("created ok not equal created result")
{
auto ok = result<int, int>::ok(12);
auto err = result<int, int>::err(12);
// REQUIRE(ok != err);
REQUIRE(ok != err);
}
SECTION("throws exception if invalid value accessed")
@ -192,7 +196,7 @@ TEST_CASE("result")
REQUIRE(ss.str() == "[err]");
}
#if MAYBE_RESULT_HAS_MOVE_ACCESSORS == 1
#if OPTIONAL_HAS_MOVE_ACCESSORS == 1
SECTION("ok move accessor works")
{