Skip to content

Instantly share code, notes, and snippets.

@Algo-ryth-mix
Created May 1, 2020 12:16
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save Algo-ryth-mix/d330c89b80903801eb3c72e33cba0c4d to your computer and use it in GitHub Desktop.
result.hpp
/**
* 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