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:
parent
cd71dd9252
commit
7ee87d5df5
23
README.md
23
README.md
@ -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
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
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")
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user