Program Listing for File result.hpp¶
↰ Return to documentation for file (/home/runner/work/Legion-Engine/Legion-Engine/legion/engine/core/common/result.hpp
)
#pragma once
#include <tuple>
#include <type_traits>
#include <memory>
#include <stdexcept>
#include <utility>
#include <functional>
#include <core/platform/platform.hpp>
namespace legion::core::common {
class result_ident {};
template <class... T>
class result;
class ok_ident {};
class err_ident {};
struct tuple_create_helper {};
template<class... Stuff> struct many_t {};
using empty_t = many_t<>;
template <class T, class Original> struct try_static_cast_result;
template <class Original> struct try_static_cast_result<void, Original> { using type = Original; };
template <class T, class Original> struct try_static_cast_result { using type = T; };
template <class T, class Original>
auto try_static_cast(const Original& o)
{
if constexpr (std::is_same<T, void>::value) return o;
else return static_cast<T>(o);
}
/********************************************************************************/
template<class... Any>
class ok_proxy;
template <> class ok_proxy<void> : public ok_ident
{
public:
operator ok_proxy<>() const;
};
template <> class ok_proxy<> : public ok_ident
{
operator ok_proxy<void>()
{
return ok_proxy<void>();
}
};
template <class T> class ok_proxy<T> : public ok_ident
{
public:
ok_proxy(ok_proxy&&) noexcept = default;
ok_proxy(T val) : m_val(std::move(val)) {}
explicit ok_proxy(const std::tuple<T>& tpl) : m_val(std::get<0>(tpl)) {}
explicit ok_proxy(tuple_create_helper, std::tuple<T>& tpl) : m_val(std::get<0>(tpl)) {}
operator T& () {
return m_val;
}
operator const T& () const {
return m_val;
}
private:
T m_val;
};
template <class T, class... Any> class ok_proxy<T, Any...> : public ok_ident
{
public:
using tuple_type = std::tuple<T, Any...>;
ok_proxy(ok_proxy&&) noexcept = default;
template <typename = std::enable_if_t<!std::is_same<T, tuple_create_helper>::value>>
ok_proxy(T val, Any... args) : ok_proxy(tuple_create_helper{}, std::make_tuple(std::move(val), std::move(args)...)) {}
explicit ok_proxy(tuple_type tpl) : m_values(std::move(tpl)) {}
explicit ok_proxy(tuple_create_helper, tuple_type tpl) :m_values(std::move(tpl)) {}
operator std::tuple<T, Any ...>() const
{
return std::move(m_values);
}
operator const std::tuple<T, Any...>& () const
{
return m_values;
}
private:
tuple_type m_values;
};
inline ok_proxy<void>::operator ok_proxy<>() const {
return ok_proxy<>();
}
inline ok_proxy<void> Ok()
{
return ok_proxy<void>{};
}
template <class T, class...Any CNDOXY(std::enable_if_t<!(std::is_base_of_v<result_ident, T> && sizeof...(Any) == 0), int> = 0)>
inline ok_proxy<T, Any...> Ok(T&& t, Any&& ... any)
{
return ok_proxy<T, Any...>(std::move(t), std::forward<Any>(any)...);
}
template <class T, class...Any CNDOXY(std::enable_if_t<!(std::is_base_of_v<result_ident, T> && sizeof...(Any) == 0), int> = 0)>
inline ok_proxy<T, Any...> Ok(T& t, Any&& ... any)
{
return ok_proxy<T, Any...>(t, std::forward<Any>(any)...);
}
template<class... Args>
inline ok_proxy<Args...> Ok(std::tuple<Args...> args)
{
return std::apply(Ok, args);
}
template <class... Args>
inline typename result<Args...>::ok_type Ok_of(result<Args...>& res)
{
return Ok(res.get());
}
/********************************************************************************/
/********************************************************************************/
template <class... Any>
class err_proxy;
template <> class err_proxy<void> : public err_ident
{
public:
err_proxy() {}
operator err_proxy<>() const;
};
template <> class err_proxy<> : public err_ident
{
operator err_proxy<void>()
{
return err_proxy<void>();
}
};
template <class T> class err_proxy<T> : public err_ident
{
public:
err_proxy(err_proxy&&) noexcept = default;
err_proxy(T val) : m_val(std::move(val)) {}
explicit err_proxy(const std::tuple<T >& tpl) : m_val(std::get<0>(tpl)) {}
explicit err_proxy(tuple_create_helper, std::tuple<T >& tpl) : m_val(std::get<0>(tpl)) {}
operator T& () {
return m_val;
}
operator const T& () const {
return m_val;
}
private:
T m_val;
};
template <class T, class... Any> class err_proxy<T, Any...> : public err_ident
{
public:
using tuple_type = std::tuple<T, Any ...>;
template <typename = std::enable_if_t<!std::is_same<T, tuple_create_helper>::value>>
err_proxy(T val, Any ... args) : err_proxy(tuple_create_helper{}, std::make_tuple(std::move(val), std::move(args...))) {}
explicit err_proxy(tuple_type tpl) : m_values(std::move(tpl)) {}
explicit err_proxy(tuple_create_helper, tuple_type tpl) :m_values(std::move(tpl)) {}
operator std::tuple<T, Any ...>& ()
{
return m_values;
}
operator const std::tuple<T, Any ...>& () const
{
return m_values;
}
private:
tuple_type m_values;
};
inline err_proxy<void>::operator err_proxy<>() const {
return err_proxy<>();
}
inline err_proxy<void> Err()
{
return err_proxy<void>{};
}
template <class T, class...Any CNDOXY(std::enable_if_t<!(std::is_base_of_v<result_ident, std::remove_reference<T>> && sizeof...(Any) == 0), int> = 0)>
inline err_proxy<T, Any...> Err(T&& t, Any&& ... any)
{
return err_proxy<T, Any...>(std::move(t), std::forward<Any>(any)...);
}
template <class T, class...Any CNDOXY(std::enable_if_t<!(std::is_base_of_v<result_ident, std::remove_reference<T>> && sizeof...(Any) == 0), int> = 0)>
inline err_proxy<T, Any...> Err(T& t, Any&& ... any)
{
return err_proxy<T, Any...>(t, std::forward<Any>(any)...);
}
template<class... Args>
inline err_proxy<Args...> Err(std::tuple<Args...> args)
{
return std::apply(Err, args);
}
template <class... Args>
inline typename result<Args...>::err_type Err_of(result<Args...>& res)
{
return Err(res.get_error());
}
/********************************************************************************/
template<class... Lots>
class result_impl;
template <class OkType, class ErrType, class OkResultType, class ErrResultType>
class result_impl<OkType, ErrType, OkResultType, ErrResultType>
{
public:
using err_type = ErrType;
using ok_type = OkType;
using err_result_t = ErrResultType;
using ok_result_t = OkResultType;
result_impl(std::unique_ptr<ok_type> ok, std::unique_ptr<err_type> err) :
m_err(std::move(err)), m_ok(std::move(ok)) {}
result_impl(const result_impl&) = delete;
result_impl(result_impl&&) noexcept = default;
result_impl& operator=(const result_impl&) = delete;
result_impl& operator=(result_impl&&) noexcept = default;
virtual ~result_impl() = default;
typename try_static_cast_result<ok_result_t, ok_type>::type get()
{
if (m_ok) return try_static_cast<ok_result_t>(std::move(*m_ok.get()));
else if (m_err) throw try_static_cast<err_result_t>(*m_err);
else throw std::runtime_error("both ok and err were empty!");
}
operator typename try_static_cast_result<ok_result_t, ok_type>::type() {
return get();
}
operator typename try_static_cast_result<ok_result_t, ok_type>::type() const {
return get();
}
template <class Func,class... Args>
auto except(Func&& f,Args&&... args) -> decltype(auto)
{
if(has_err())
{
return std::invoke(f,get_error(),std::forward<Args>(args)...);
}
return get();
}
template <class Func,class... Args>
auto except(const Func& f,Args&&... args) -> decltype(auto)
{
if(has_err())
{
return std::invoke(f,get_error(),std::forward<Args>(args)...);
}
return get();
}
L_NODISCARD bool valid() const noexcept
{
return m_ok != nullptr;
}
bool has_err() noexcept
{
return m_err != nullptr && m_ok == nullptr;
}
L_NODISCARD const typename try_static_cast_result<err_result_t, err_type>::type& get_error() const
{
if (m_err) return try_static_cast<err_result_t>(*m_err);
throw std::runtime_error("this result would have been valid!");
}
L_NODISCARD typename try_static_cast_result<err_result_t, err_type>::type get_error()
{
if (m_err) return try_static_cast<err_result_t>(*m_err);
throw std::runtime_error("this result would have been valid!");
}
L_NORETURN void rethrow()
{
throw try_static_cast<err_result_t>(*m_err);
}
void maybe_rethrow()
{
if (has_err()) rethrow();
}
protected:
std::unique_ptr<err_type> m_err;
std::unique_ptr<ok_type> m_ok;
};
template <class ErrType, class OkResultType, class ErrResultType>
class result_impl<void, ErrType, OkResultType, ErrResultType>
{
public:
using err_type = ErrType;
using ok_type = void;
using err_result_t = ErrResultType;
using ok_result_t = OkResultType;
result_impl(std::unique_ptr<ok_type> ok, std::unique_ptr<err_type> err) :
m_err(std::move(err)), m_ok(std::move(ok)) {}
result_impl(const result_impl&) = delete;
result_impl(result_impl&&) noexcept = default;
result_impl& operator=(const result_impl&) = delete;
result_impl& operator=(result_impl&&) noexcept = default;
virtual ~result_impl() = default;
template <class Func, class... Args>
void except(Func && f, Args&&... args)
{
if (has_err())
{
return std::invoke(f, get_error(), std::forward<Args>(args)...);
}
}
template <class Func, class... Args>
void except(const Func & f, Args&&... args)
{
if (has_err())
{
return std::invoke(f, get_error(), std::forward<Args>(args)...);
}
}
L_NODISCARD bool valid() const noexcept
{
return m_ok != nullptr;
}
bool has_err() noexcept
{
return m_err != nullptr && m_ok == nullptr;
}
L_NODISCARD const typename try_static_cast_result<err_result_t, err_type>::type& get_error() const
{
if (m_err) return try_static_cast<err_result_t>(*m_err);
throw std::runtime_error("this result would have been valid!");
}
L_NODISCARD typename try_static_cast_result<err_result_t, err_type>::type get_error()
{
if (m_err) return try_static_cast<err_result_t>(*m_err);
throw std::runtime_error("this result would have been valid!");
}
L_NORETURN void rethrow()
{
throw try_static_cast<err_result_t>(*m_err);
}
void maybe_rethrow()
{
if (has_err()) rethrow();
}
protected:
std::unique_ptr<err_type> m_err;
std::unique_ptr<ok_type> m_ok;
};
template <class... OkArgs, class... ErrArgs>
class result<many_t<OkArgs...>, many_t<ErrArgs...>> :
public result_impl<ok_proxy<OkArgs...>, err_proxy<ErrArgs...>, std::tuple<OkArgs...>, std::tuple<ErrArgs...>>,
public result_ident {
public:
using rimpl = result_impl<ok_proxy<OkArgs...>, err_proxy<ErrArgs...>, std::tuple<OkArgs...>, std::tuple<ErrArgs...>>;
result(ok_proxy<OkArgs...> ok) : rimpl((std::make_unique<ok_proxy<OkArgs...>>(std::move(ok))), nullptr) {};
result(err_proxy<ErrArgs...> err) : rimpl(nullptr, (std::make_unique<err_proxy<ErrArgs...>>(std::move(err)))) {};
using rimpl::operator typename try_static_cast_result<std::tuple<OkArgs...>, ok_proxy<OkArgs...>>::type;
};
template <class ErrType, class... Args>
class result<many_t<Args...>, ErrType> :
public result_impl<ok_proxy<Args...>, err_proxy<ErrType>, std::tuple<Args...>, ErrType>,
public result_ident {
public:
using rimpl = result_impl<ok_proxy<Args...>, err_proxy<ErrType>, std::tuple<Args...>, ErrType>;
result(ok_proxy<Args...> ok) : rimpl((std::make_unique<ok_proxy<Args...>>(std::move(ok))), nullptr) {};
result(err_proxy<ErrType> err) : rimpl(nullptr, (std::make_unique<err_proxy<ErrType>>(std::move(err)))) {};
using rimpl::operator typename try_static_cast_result<std::tuple<Args...>, ok_proxy<Args...>>::type;
};
template <class OkType, class... Args>
class result<OkType, many_t<Args...>> :
public result_impl<ok_proxy<OkType>, err_proxy<Args...>, OkType, std::tuple<Args...>>,
public result_ident {
public:
using rimpl = result_impl<ok_proxy<OkType>, err_proxy<Args...>, OkType, std::tuple<Args...>>;
result(ok_proxy<OkType> ok) : rimpl((std::make_unique<ok_proxy<OkType>>(std::move(ok))), nullptr) {};
result(err_proxy<Args...> err) : rimpl(nullptr, (std::make_unique<err_proxy<Args...>>(std::move(err)))) {};
using rimpl::operator typename try_static_cast_result<OkType, ok_proxy<OkType>>::type;
};
template <class OkType, class ErrType>
class result<OkType, ErrType> :
public result_impl<ok_proxy<OkType>, err_proxy<ErrType>, OkType, ErrType>,
public result_ident {
public:
using rimpl = result_impl<ok_proxy<OkType>, err_proxy<ErrType>, OkType, ErrType>;
result(ok_proxy<OkType> ok) : rimpl((std::make_unique<ok_proxy<OkType>>(std::move(ok))), nullptr) {};
result(err_proxy<ErrType> err) : rimpl(nullptr, (std::make_unique<err_proxy<ErrType>>(std::move(err)))) {};
using rimpl::operator typename try_static_cast_result<OkType, ok_proxy<OkType>>::type;
};
class valid_t {};
template <class Result>
class result_decay
{
public:
using ok_type = typename Result::ok_result_t;
using err_type = typename Result::err_result_t;
result_decay(Result r) : m_r{ std::move(r) } {}
bool operator==(valid_t)
{
return m_r.valid();
}
bool operator!=(valid_t)
{
return m_r.has_err();
}
bool operator==(std::nullptr_t)
{
return m_r.has_err();
}
bool operator!=(std::nullptr_t)
{
return m_r.valid();
}
operator ok_type ()
{
return m_r.get();
}
operator Result ()
{
return m_r;
}
auto decay() -> decltype(auto)
{
return m_r.get();
}
template <class Func,class... Args>
auto except(Func&& f,Args&&... args) -> decltype(auto)
{
return m_r.except(std::forward<Func>(f),std::forward<Args>(args)...);
}
template <class Func,class... Args>
auto except(const Func& f,Args&&... args) -> decltype(auto)
{
return m_r.except(f,std::forward<Args>(args)...);
}
err_type get_error()
{
return m_r.get_error();
}
private:
Result m_r;
};
template <class...Args>
using result_decay_more = result_decay<result<Args...>>;
template <class T,class E>
auto decay(result_decay_more<T,E>& x) ->decltype(auto) { return x.get(); }
constexpr valid_t valid{};
}