379 lines
8.3 KiB
C++
379 lines
8.3 KiB
C++
|
|
/*
|
|||
|
|
* Result.hpp
|
|||
|
|
*
|
|||
|
|
* Created on: 11 May 2023
|
|||
|
|
* Author: malyarenko
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD> C++ <EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> header-only <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Result
|
|||
|
|
* https://github.com/oktal/result
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#ifndef UMLIBRARY_COMMON_RESULT_HH_
|
|||
|
|
#define UMLIBRARY_COMMON_RESULT_HH_
|
|||
|
|
|
|||
|
|
#include <cstddef>
|
|||
|
|
#include <cstdlib>
|
|||
|
|
#include <utility>
|
|||
|
|
#include <new>
|
|||
|
|
#include <type_traits>
|
|||
|
|
|
|||
|
|
#include "Error.hh"
|
|||
|
|
|
|||
|
|
namespace result {
|
|||
|
|
namespace types {
|
|||
|
|
|
|||
|
|
template< typename T >
|
|||
|
|
struct Ok {
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
Ok(T&& value)
|
|||
|
|
: value(std::move(value)) { }
|
|||
|
|
#else
|
|||
|
|
Ok(T& value)
|
|||
|
|
: value(value) { }
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
T value;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
template<>
|
|||
|
|
struct Ok<void> { };
|
|||
|
|
|
|||
|
|
template< typename E >
|
|||
|
|
struct Err {
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
Err(E&& value)
|
|||
|
|
: value(std::move(value)) { }
|
|||
|
|
#else
|
|||
|
|
Err(E& value)
|
|||
|
|
: value(value) { }
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
Err(const E& value)
|
|||
|
|
: value(value) { }
|
|||
|
|
|
|||
|
|
E value;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
} /* namespace types */
|
|||
|
|
} /* namespace result */
|
|||
|
|
|
|||
|
|
inline result::types::Ok<void> Ok() {
|
|||
|
|
return result::types::Ok<void>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
template<typename T, typename CleanT = typename std::decay<T>::type>
|
|||
|
|
result::types::Ok<CleanT> Ok(T&& value) {
|
|||
|
|
return result::types::Ok<CleanT>(std::forward<CleanT>(value));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename E, typename CleanE = typename std::decay<E>::type>
|
|||
|
|
result::types::Err<CleanE> Err(E&& value) {
|
|||
|
|
return result::types::Err<CleanE>(std::forward<CleanE>(value));
|
|||
|
|
}
|
|||
|
|
#else
|
|||
|
|
template<typename T, typename CleanT = typename std::tr1::decay<T>::type>
|
|||
|
|
result::types::Ok<CleanT> Ok(T& value) {
|
|||
|
|
return result::types::Ok<CleanT>(value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename E, typename CleanE = typename std::tr1::decay<E>::type>
|
|||
|
|
result::types::Err<CleanE> Err(E& value) {
|
|||
|
|
return result::types::Err<CleanE>(value);
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
namespace result {
|
|||
|
|
namespace storage {
|
|||
|
|
|
|||
|
|
struct OkTag { };
|
|||
|
|
|
|||
|
|
struct ErrTag { };
|
|||
|
|
|
|||
|
|
template< typename T, typename E >
|
|||
|
|
class Storage {
|
|||
|
|
public:
|
|||
|
|
Storage()
|
|||
|
|
: initialized(false) { }
|
|||
|
|
|
|||
|
|
void construct(result::types::Ok<T> ok) {
|
|||
|
|
new (&storage) T(ok.value);
|
|||
|
|
initialized = true;
|
|||
|
|
}
|
|||
|
|
void construct(result::types::Err<E> err) {
|
|||
|
|
new (&storage) E(err.value);
|
|||
|
|
initialized = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
template< typename U >
|
|||
|
|
void constructRaw(U&& value) {
|
|||
|
|
typedef typename std::decay<U>::type CleanU;
|
|||
|
|
|
|||
|
|
new (&storage) CleanU(std::forward<U>(value));
|
|||
|
|
initialized = true;
|
|||
|
|
}
|
|||
|
|
#else
|
|||
|
|
template< typename U >
|
|||
|
|
void constructRaw(U& value) {
|
|||
|
|
typedef typename std::tr1::decay<U>::type CleanU;
|
|||
|
|
|
|||
|
|
new (&storage) CleanU(value);
|
|||
|
|
initialized = true;
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
template< typename U >
|
|||
|
|
const U& get() const {
|
|||
|
|
return *reinterpret_cast< const U * >(&storage);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template< typename U >
|
|||
|
|
U& get() {
|
|||
|
|
return *reinterpret_cast< U * >(&storage);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void destroy(OkTag) {
|
|||
|
|
if (initialized) {
|
|||
|
|
get<T>().~T();
|
|||
|
|
initialized = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void destroy(ErrTag) {
|
|||
|
|
if (initialized) {
|
|||
|
|
get<E>().~E();
|
|||
|
|
initialized = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
static constexpr std::size_t Size = sizeof(T) > sizeof(E) ? sizeof(T) : sizeof(E);
|
|||
|
|
static constexpr std::size_t Align = alignof(T) > alignof(E) ? alignof(T) : alignof(E);
|
|||
|
|
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
typedef typename std::aligned_storage<Size, Align>::type type;
|
|||
|
|
type storage;
|
|||
|
|
#else
|
|||
|
|
unsigned long storage[1+Size/2];
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
bool initialized;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
template< typename E >
|
|||
|
|
struct Storage< void, E > {
|
|||
|
|
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
typedef typename std::aligned_storage<sizeof(E), alignof(E)>::type type;
|
|||
|
|
type storage;
|
|||
|
|
#else
|
|||
|
|
unsigned long storage[1+sizeof(E)/2];
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
void construct(result::types::Ok<void>) {
|
|||
|
|
initialized = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void construct(result::types::Err<E> err) {
|
|||
|
|
new (&storage) E(err.value);
|
|||
|
|
initialized = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
template< typename U >
|
|||
|
|
void constructRaw(U&& value) {
|
|||
|
|
typedef typename std::decay<U>::type CleanU;
|
|||
|
|
|
|||
|
|
new (&storage) CleanU(std::forward<U>(value));
|
|||
|
|
initialized = true;
|
|||
|
|
}
|
|||
|
|
#else
|
|||
|
|
template< typename U >
|
|||
|
|
void constructRaw(U& value) {
|
|||
|
|
typedef typename std::tr1::decay<U>::type CleanU;
|
|||
|
|
|
|||
|
|
new (&storage) CleanU(value);
|
|||
|
|
initialized = true;
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
void destroy(OkTag) {
|
|||
|
|
initialized = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void destroy(ErrTag) {
|
|||
|
|
if (initialized) {
|
|||
|
|
get<E>().~E();
|
|||
|
|
initialized = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename U>
|
|||
|
|
const U& get() const {
|
|||
|
|
return *reinterpret_cast< const U * >(&storage);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename U>
|
|||
|
|
U& get() {
|
|||
|
|
return *reinterpret_cast< U * >(&storage);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool initialized;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
template< typename T, typename E >
|
|||
|
|
struct Constructor {
|
|||
|
|
static void copy(const Storage<T, E>& src, Storage<T, E>& dst, OkTag) {
|
|||
|
|
dst.constructRaw(src.template get<T>());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
static void copy(const Storage<T, E>& src, Storage<T, E>& dst, ErrTag) {
|
|||
|
|
dst.constructRaw(src.template get<E>());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
static void move(Storage<T, E>&& src, Storage<T, E>& dst, OkTag) {
|
|||
|
|
dst.constructRaw(std::move(src.template get<T>()));
|
|||
|
|
src.destroy(OkTag());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void move(Storage<T, E>&& src, Storage<T, E>& dst, ErrTag) {
|
|||
|
|
dst.constructRaw(std::move(src.template get<E>()));
|
|||
|
|
src.destroy(ErrTag());
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
template<typename E>
|
|||
|
|
struct Constructor<void, E> {
|
|||
|
|
static void copy(const Storage<void, E>& src, Storage<void, E>& dst, OkTag) { }
|
|||
|
|
|
|||
|
|
static void copy(const Storage<void, E>& src, Storage<void, E>& dst, ErrTag) {
|
|||
|
|
dst.constructRaw(src.template get<E>());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
static void move(Storage<void, E>&& src, Storage<void, E>& dst, OkTag) { }
|
|||
|
|
|
|||
|
|
static void move(Storage<void, E>&& src, Storage<void, E>& dst, ErrTag) {
|
|||
|
|
dst.constructRaw(std::move(src.template get<E>()));
|
|||
|
|
src.destroy(ErrTag());
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
} /* namespace storage */
|
|||
|
|
} /* namespace result */
|
|||
|
|
|
|||
|
|
template< typename T, typename E >
|
|||
|
|
class Result {
|
|||
|
|
public:
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
static_assert(!std::is_same<E, void>::value, "void error type is not allowed");
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
typedef result::storage::Storage<T, E> storage_type;
|
|||
|
|
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
Result(result::types::Ok<T> ok)
|
|||
|
|
: ok(true)
|
|||
|
|
{
|
|||
|
|
storage.construct(std::move(ok));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Result(result::types::Err<E> err)
|
|||
|
|
: ok(false)
|
|||
|
|
{
|
|||
|
|
storage.construct(std::move(err));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Result(Result&& other) {
|
|||
|
|
if (other.isOk()) {
|
|||
|
|
result::storage::Constructor<T, E>::move(std::move(other.storage), storage, result::storage::OkTag());
|
|||
|
|
ok = true;
|
|||
|
|
} else {
|
|||
|
|
result::storage::Constructor<T, E>::move(std::move(other.storage), storage, result::storage::ErrTag());
|
|||
|
|
ok = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#else
|
|||
|
|
Result(result::types::Ok<T> ok)
|
|||
|
|
: ok(true)
|
|||
|
|
{
|
|||
|
|
storage.construct(ok);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Result(result::types::Err<E> err)
|
|||
|
|
: ok(false)
|
|||
|
|
{
|
|||
|
|
storage.construct(err);
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
Result(const Result& other) {
|
|||
|
|
if (other.isOk()) {
|
|||
|
|
result::storage::Constructor<T, E>::copy(other.storage, storage, result::storage::OkTag());
|
|||
|
|
ok = true;
|
|||
|
|
} else {
|
|||
|
|
result::storage::Constructor<T, E>::copy(other.storage, storage, result::storage::ErrTag());
|
|||
|
|
ok = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
~Result() {
|
|||
|
|
if (ok) {
|
|||
|
|
storage.destroy(result::storage::OkTag());
|
|||
|
|
} else {
|
|||
|
|
storage.destroy(result::storage::ErrTag());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool isOk() const {
|
|||
|
|
return ok;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool isErr() const {
|
|||
|
|
return !ok;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if __cplusplus >= 201103L
|
|||
|
|
template< typename U = T >
|
|||
|
|
typename std::enable_if<!std::is_same<U, void>::value, U >::type
|
|||
|
|
unwrap() const {
|
|||
|
|
if (isOk()) {
|
|||
|
|
return storage.template get<U>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::abort();
|
|||
|
|
}
|
|||
|
|
#else
|
|||
|
|
template< typename U = T >
|
|||
|
|
typename std::tr1::enable_if<!std::tr1::is_same<U, void>::value, U >::type
|
|||
|
|
unwrap() const {
|
|||
|
|
if (isOk()) {
|
|||
|
|
return storage.template get<U>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::abort();
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
E unwrapErr() const {
|
|||
|
|
if (isErr()) {
|
|||
|
|
return storage.template get<E>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::abort();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
bool ok;
|
|||
|
|
storage_type storage;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
#endif /* UMLIBRARY_COMMON_RESULT_HH_ */
|