Created
May 1, 2020 12:16
Star
You must be signed in to star a gist
result.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright 2020 Raphael Baier | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files | |
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, | |
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do | |
* so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO | |
* THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
* DEALINGS IN THE SOFTWARE. | |
*/ | |
#ifndef TEST_RESULT_H | |
#define TEST_RESULT_H | |
#include <tuple> | |
#include <type_traits> | |
#include <memory> | |
#include <utility> | |
namespace pixl::util { | |
class ok_ident{}; | |
class err_ident{}; | |
struct tuple_create_helper{}; | |
template<class... Stuff> struct Many{}; | |
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); | |
} | |
/********************************************************************************/ | |
/**@brief Ok template defs*/ | |
template<class... Any> | |
class Ok_; | |
template <> class Ok_<void> : public ok_ident | |
{ | |
public: | |
Ok_(Ok_&&) noexcept = default; | |
Ok_() {} | |
operator Ok_<>(); | |
}; | |
template <> class Ok_<> : public ok_ident | |
{ | |
operator Ok_<void>() | |
{ | |
return Ok_<void>(); | |
} | |
}; | |
template <class T> class Ok_<T> : public ok_ident | |
{ | |
public: | |
Ok_(Ok_&&) noexcept = default; | |
Ok_(T val) : m_val(std::move(val)) {} | |
explicit Ok_(const std::tuple<T>& tpl) : m_val(std::get<0>(tpl)) {} | |
explicit Ok_(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_<T,Any...> : public ok_ident | |
{ | |
public: | |
using tuple_type = std::tuple<T,Any...>; | |
Ok_(Ok_&&) noexcept = default; | |
template <typename = std::enable_if_t<!std::is_same<T,tuple_create_helper>::value>> | |
Ok_(T val, Any&&... args) : Ok_(std::make_tuple(std::move(val),args...)) {} | |
explicit Ok_(tuple_type tpl) : m_values(std::move(tpl)) {} | |
explicit Ok_(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; | |
}; | |
Ok_<void>::operator Ok_<>() { | |
return Ok_<>(); | |
} | |
inline Ok_<void> Ok() | |
{ | |
return Ok_<void>{}; | |
} | |
template <class T,class...Any> | |
inline Ok_<T,Any...> Ok(T t,Any&&... any) | |
{ | |
return Ok_<T,Any...>(std::forward<T>(t),std::forward<Any>(any)...); | |
} | |
/********************************************************************************/ | |
/********************************************************************************/ | |
/**@brief Err template defs*/ | |
template <class... Any> | |
class Err_; | |
template <> class Err_<void> : public err_ident | |
{ | |
public: | |
Err_() {} | |
operator Err_<>(); | |
}; | |
template <> class Err_<> : public err_ident | |
{ | |
operator Err_<void>() | |
{ | |
return Err_<void>(); | |
} | |
}; | |
template <class T> class Err_<T> : public err_ident | |
{ | |
public: | |
Err_(Err_&&) noexcept = default; | |
Err_(T val) : m_val(std::move(val)) {} | |
explicit Err_(const std::tuple<T>& tpl) : m_val(std::get<0>(tpl)) {} | |
explicit Err_(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_<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_(T val, Any&&... args) : Err_(std::make_tuple(std::move(val),args...)) {} | |
explicit Err_(const tuple_type& tpl) : m_values(tpl) {} | |
explicit Err_(tuple_create_helper,const tuple_type& tpl) :m_values(tpl){} | |
operator std::tuple<T,Any...>&() | |
{ | |
return m_values; | |
} | |
operator const std::tuple<T,Any...>&() const | |
{ | |
return m_values; | |
} | |
private: | |
tuple_type m_values; | |
}; | |
Err_<void>::operator Err_<>() { | |
return Err_<>(); | |
} | |
inline Err_<void> Err() | |
{ | |
return Err_<void>{}; | |
} | |
template <class T,class...Any> | |
inline Err_<T,Any...> Err(T t,Any&&... any) | |
{ | |
return Err_<T,Any...>(std::forward<T>(t),std::forward<Any>(any)...); | |
} | |
/********************************************************************************/ | |
template <class... T> | |
class Result; | |
template<class... Lots> | |
class ResultImpl; | |
template <class OkType,class ErrType, class OkResultType,class ErrResultType> | |
class ResultImpl<OkType,ErrType,OkResultType,ErrResultType> | |
{ | |
public: | |
using err_type = ErrType; | |
using ok_type = OkType; | |
using err_result_t = ErrResultType; | |
using ok_result_t = OkResultType; | |
ResultImpl(std::unique_ptr<ok_type>&& ok,std::unique_ptr<err_type>&& err) : | |
m_ok(std::move(ok)), m_err(std::move(err)) {} | |
ResultImpl(const ResultImpl&) = default; | |
ResultImpl(ResultImpl&&) noexcept = default; | |
ResultImpl& operator=(const ResultImpl&) = default; | |
ResultImpl& operator=(ResultImpl&&) noexcept = default; | |
virtual ~ ResultImpl() = default; | |
auto get() -> decltype(auto) | |
{ | |
if(m_ok) return try_static_cast<ok_result_t>(*m_ok); | |
else if(m_err) throw try_static_cast<err_result_t>(*m_err); | |
else throw std::runtime_error("both ok and err were empty!"); | |
} | |
auto get() const -> decltype(auto) | |
{ | |
if(m_ok) return try_static_cast<ok_result_t>(*m_ok); | |
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(); | |
} | |
bool valid() noexcept | |
{ | |
return m_ok; | |
} | |
bool has_err() noexcept | |
{ | |
return m_err && !m_ok; | |
} | |
const auto& 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!"); | |
} | |
[[noreturn]] void rethrow() | |
{ | |
throw get_error(); | |
} | |
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<OkArgs...>,Many<ErrArgs...>> : | |
public ResultImpl<Ok_<OkArgs...>,Err_<ErrArgs...>,std::tuple<OkArgs...>,std::tuple<ErrArgs...>>{ | |
public: | |
Result(Ok_<OkArgs...>&& ok) : ResultImpl(std::move(std::make_unique<Ok_<OkArgs...>>(std::move(ok))),nullptr){}; | |
Result(Err_<ErrArgs...>&& err) : ResultImpl(nullptr,std::move(std::make_unique<Err_<ErrArgs...>>(std::move(err)))){}; | |
using rimpl = ResultImpl<Ok_<OkArgs...>,Err_<ErrArgs...>,std::tuple<OkArgs...>,std::tuple<ErrArgs...>>; | |
using rimpl::operator typename try_static_cast_result<std::tuple<OkArgs...>,Ok_<OkArgs...>>::type; | |
}; | |
template <class ErrType,class... Args> | |
class Result<Many<Args...>,ErrType> : | |
public ResultImpl<Ok_<Args...>,Err_<ErrType>,std::tuple<Args...>,ErrType>{ | |
public: | |
Result(Ok_<Args...>&& ok) : ResultImpl(std::move(std::make_unique<Ok_<Args...>>(std::move(ok))),nullptr){}; | |
Result(Err_<ErrType>&& err) : ResultImpl(nullptr,std::move(std::make_unique<Err_<ErrType>>(std::move(err)))){}; | |
using rimpl = ResultImpl<Ok_<Args...>,Err_<ErrType>,std::tuple<Args...>,ErrType>; | |
using rimpl::operator typename try_static_cast_result<std::tuple<Args...>,Ok_<Args...>>::type; | |
}; | |
template <class OkType,class... Args> | |
class Result<OkType,Many<Args...>> : | |
public ResultImpl<Ok_<OkType>,Err_<Args...>,OkType,std::tuple<Args...>>{ | |
public: | |
Result(Ok_<OkType>&& ok) : ResultImpl(std::move(std::make_unique<Ok_<OkType>>(std::move(ok))),nullptr){}; | |
Result(Err_<Args...>&& err) : ResultImpl(nullptr,std::move(std::make_unique<Err_<Args...>>(std::move(err)))){}; | |
using rimpl = ResultImpl<Ok_<OkType>,Err_<Args...>,OkType,std::tuple<Args...>>; | |
using rimpl::operator typename try_static_cast_result<OkType,Ok_<OkType>>::type; | |
}; | |
template <class OkType,class ErrType> | |
class Result<OkType,ErrType> : | |
public ResultImpl<Ok_<OkType>,Err_<ErrType>,OkType,ErrType>{ | |
public: | |
Result(Ok_<OkType>&& ok) : ResultImpl(std::move(std::make_unique<Ok_<OkType>>(std::move(ok))),nullptr){}; | |
Result(Err_<ErrType>&& err) : ResultImpl(nullptr,std::move(std::make_unique<Err_<ErrType>>(std::move(err)))){}; | |
using rimpl = ResultImpl<Ok_<OkType>,Err_<ErrType>,OkType,ErrType>; | |
using rimpl::operator typename try_static_cast_result<OkType,Ok_<OkType>>::type; | |
}; | |
} | |
#endif //TEST_RESULT_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment