/* * Result.hpp * * Created on: 11 May 2023 * Author: malyarenko */ /* * Адаптированная под C++ и сокращённая header-only библиотека Result * https://github.com/oktal/result */ #ifndef UMLIBRARY_COMMON_RESULT_HH_ #define UMLIBRARY_COMMON_RESULT_HH_ #include #include #include #include #include #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 { }; 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 Ok() { return result::types::Ok(); } #if __cplusplus >= 201103L template::type> result::types::Ok Ok(T&& value) { return result::types::Ok(std::forward(value)); } template::type> result::types::Err Err(E&& value) { return result::types::Err(std::forward(value)); } #else template::type> result::types::Ok Ok(T& value) { return result::types::Ok(value); } template::type> result::types::Err Err(E& value) { return result::types::Err(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 ok) { new (&storage) T(ok.value); initialized = true; } void construct(result::types::Err err) { new (&storage) E(err.value); initialized = true; } #if __cplusplus >= 201103L template< typename U > void constructRaw(U&& value) { typedef typename std::decay::type CleanU; new (&storage) CleanU(std::forward(value)); initialized = true; } #else template< typename U > void constructRaw(U& value) { typedef typename std::tr1::decay::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(); initialized = false; } } void destroy(ErrTag) { if (initialized) { get().~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::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::type type; type storage; #else unsigned long storage[1+sizeof(E)/2]; #endif void construct(result::types::Ok) { initialized = true; } void construct(result::types::Err err) { new (&storage) E(err.value); initialized = true; } #if __cplusplus >= 201103L template< typename U > void constructRaw(U&& value) { typedef typename std::decay::type CleanU; new (&storage) CleanU(std::forward(value)); initialized = true; } #else template< typename U > void constructRaw(U& value) { typedef typename std::tr1::decay::type CleanU; new (&storage) CleanU(value); initialized = true; } #endif void destroy(OkTag) { initialized = false; } void destroy(ErrTag) { if (initialized) { get().~E(); initialized = false; } } template const U& get() const { return *reinterpret_cast< const U * >(&storage); } template U& get() { return *reinterpret_cast< U * >(&storage); } bool initialized; }; template< typename T, typename E > struct Constructor { static void copy(const Storage& src, Storage& dst, OkTag) { dst.constructRaw(src.template get()); } static void copy(const Storage& src, Storage& dst, ErrTag) { dst.constructRaw(src.template get()); } #if __cplusplus >= 201103L static void move(Storage&& src, Storage& dst, OkTag) { dst.constructRaw(std::move(src.template get())); src.destroy(OkTag()); } static void move(Storage&& src, Storage& dst, ErrTag) { dst.constructRaw(std::move(src.template get())); src.destroy(ErrTag()); } #endif }; template struct Constructor { static void copy(const Storage& src, Storage& dst, OkTag) { } static void copy(const Storage& src, Storage& dst, ErrTag) { dst.constructRaw(src.template get()); } #if __cplusplus >= 201103L static void move(Storage&& src, Storage& dst, OkTag) { } static void move(Storage&& src, Storage& dst, ErrTag) { dst.constructRaw(std::move(src.template get())); src.destroy(ErrTag()); } #endif }; } /* namespace storage */ } /* namespace result */ template< typename T, typename E > class Result { public: #if __cplusplus >= 201103L static_assert(!std::is_same::value, "void error type is not allowed"); #endif typedef result::storage::Storage storage_type; #if __cplusplus >= 201103L Result(result::types::Ok ok) : ok(true) { storage.construct(std::move(ok)); } Result(result::types::Err err) : ok(false) { storage.construct(std::move(err)); } Result(Result&& other) { if (other.isOk()) { result::storage::Constructor::move(std::move(other.storage), storage, result::storage::OkTag()); ok = true; } else { result::storage::Constructor::move(std::move(other.storage), storage, result::storage::ErrTag()); ok = false; } } #else Result(result::types::Ok ok) : ok(true) { storage.construct(ok); } Result(result::types::Err err) : ok(false) { storage.construct(err); } #endif Result(const Result& other) { if (other.isOk()) { result::storage::Constructor::copy(other.storage, storage, result::storage::OkTag()); ok = true; } else { result::storage::Constructor::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::value, U >::type unwrap() const { if (isOk()) { return storage.template get(); } std::abort(); } #else template< typename U = T > typename std::tr1::enable_if::value, U >::type unwrap() const { if (isOk()) { return storage.template get(); } std::abort(); } #endif E unwrapErr() const { if (isErr()) { return storage.template get(); } std::abort(); } private: bool ok; storage_type storage; }; #endif /* UMLIBRARY_COMMON_RESULT_HH_ */