You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

430 lines
7.4 KiB

#pragma once
#include <exception>
#include <type_traits>
#include "aligned_union.h"
namespace vlc
{
class TryException : public std::logic_error
{
public:
using std::logic_error::logic_error;
};
class TryUninitialized : public TryException
{
public:
TryUninitialized() : TryException("Try is not initialized")
{
}
};
template<typename T> class Try
{
enum class State : int32_t
{
None,
Success,
Failure
};
public:
using value_type = T;
Try() : m_state(State::None)
{
}
~Try()
{
switch (m_state)
{
case State::Success:
reinterpret_cast<T*>(&m_storage)->~T();
break;
case State::Failure:
reinterpret_cast<std::exception_ptr*>(&m_storage)->~exception_ptr();
break;
case State::None:
break;
}
}
explicit Try(const T& v) : m_state(State::Success)
{
static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible");
new(&m_storage) T(v);
}
explicit Try(T&& v) : m_state(State::Success)
{
new(&m_storage) T(std::forward<T>(v));
}
explicit Try(std::exception_ptr exc) : m_state(State::Failure)
{
new(&m_storage) std::exception_ptr(exc);
}
Try(const Try& src) : m_state(src.m_state)
{
static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible");
switch (m_state)
{
case State::Success:
new(&m_storage) T(*reinterpret_cast<T*>(&src.m_storage));
break;
case State::Failure:
new(&m_storage) std::exception_ptr(*reinterpret_cast<std::exception_ptr*>(&src.m_storage));
break;
case State::None:
break;
}
}
Try& operator = (const Try& src)
{
static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible");
m_state = src.m_state;
switch (m_state)
{
case State::Success:
new(&m_storage) T(*reinterpret_cast<T*>(&src.m_storage));
break;
case State::Failure:
new(&m_storage) std::exception_ptr(*reinterpret_cast<std::exception_ptr*>(&src.m_storage));
break;
case State::None:
break;
}
return *this;
}
Try(Try&& src) : m_state(src.m_state)
{
switch (m_state)
{
case State::Success:
new(&m_storage) T(std::move(*reinterpret_cast<T*>(&src.m_storage)));
break;
case State::Failure:
new(&m_storage) std::exception_ptr(std::move(*reinterpret_cast<std::exception_ptr*>(&src.m_storage)));
break;
case State::None:
break;
}
}
Try& operator = (Try&& src)
{
m_state = src.m_state;
switch (m_state)
{
case State::Success:
new(&m_storage) T(std::move(*reinterpret_cast<T*>(&src.m_storage)));
break;
case State::Failure:
new(&m_storage) std::exception_ptr(std::move(*reinterpret_cast<std::exception_ptr*>(&src.m_storage)));
break;
case State::None:
break;
}
return *this;
}
T& value() &
{
throwIfFailure();
return *reinterpret_cast<T*>(&m_storage);
}
const T& value() const &
{
throwIfFailure();
return *reinterpret_cast<const T*>(&m_storage);
}
T&& value() &&
{
throwIfFailure();
return std::move(*reinterpret_cast<T*>(&m_storage));
}
const T& operator * () const
{
throwIfFailure();
return value();
}
T& operator * ()
{
throwIfFailure();
return value();
}
const T* operator -> () const
{
throwIfFailure();
return &value();
}
T* operator -> ()
{
throwIfFailure();
return &value();
}
std::exception_ptr& exception()
{
return *reinterpret_cast<std::exception_ptr*>(&m_storage);
}
const std::exception_ptr& exception() const
{
return *reinterpret_cast<const std::exception_ptr*>(&m_storage);
}
bool isSuccess() const
{
return m_state == State::Success;
}
bool isFailure() const
{
return m_state == State::Failure;
}
template<bool isTry, typename R>
typename std::enable_if<isTry, R>::type
get()
{
return std::forward<R>(*this);
}
template<bool isTry, typename R>
typename std::enable_if<!isTry, R>::type
get()
{
return std::forward<R>(value());
}
template<typename Exc, typename F> bool recoverWith(F&& func) const
{
if (!isFailure())
{
return false;
}
try
{
std::rethrow_exception(exception());
}
catch (Exc& e)
{
func(e);
return true;
}
catch (...)
{
}
return false;
}
private:
State m_state;
typename vlc::aligned_union<4, T, std::exception_ptr>::type m_storage;
void throwIfFailure() const
{
switch (m_state)
{
case State::Failure:
std::rethrow_exception(exception());
break;
case State::None:
throw TryUninitialized();
break;
case State::Success:
break;
}
}
};
template<> class Try<void>
{
public:
using value_type = void;
Try() : m_has_value(true)
{
}
explicit Try(std::exception_ptr exc) : m_has_value(false), m_exception(exc)
{
}
Try(const Try& src) : m_has_value(src.m_has_value), m_exception(src.m_exception)
{
}
Try& operator = (const Try& src)
{
m_has_value = src.m_has_value;
m_exception = src.m_exception;
return *this;
}
void value() const
{
throwIfFailure();
}
void operator * () const
{
return value();
}
bool isSuccess() const
{
return m_has_value;
}
bool isFailure() const
{
return !isSuccess();
}
std::exception_ptr& exception()
{
return m_exception;
}
const std::exception_ptr& exception() const
{
return m_exception;
}
template<typename Exc, typename F> bool recoverWith(F&& func) const
{
if (!isFailure())
{
return false;
}
try
{
std::rethrow_exception(exception());
}
catch (Exc& e)
{
func(e);
return true;
}
catch (...)
{
}
return false;
}
template <bool, typename R>
R get()
{
return std::forward<R>(*this);
}
private:
bool m_has_value;
std::exception_ptr m_exception;
void throwIfFailure() const
{
if (!m_has_value)
{
if (m_exception)
{
std::rethrow_exception(m_exception);
}
else
{
throw std::logic_error("Exception is not set");
}
}
}
};
template<typename F>
typename std::enable_if<
!std::is_same<typename std::result_of<F()>::type, void>::value,
Try<typename std::result_of<F()>::type>>::type
make_try_with(F&& f)
{
using Result = typename std::result_of<F()>::type;
try
{
return Try<Result>(f());
}
catch (...)
{
return Try<Result>(std::current_exception());
}
}
template<typename F>
typename std::enable_if<
std::is_same<typename std::result_of<F()>::type, void>::value,
Try<void>>::type
make_try_with(F&& f)
{
try
{
f();
return Try<void>();
}
catch (...)
{
return Try<void>(std::current_exception());
}
}
namespace detail
{
template<typename... T> struct UnwrapHelper
{
// Build list of tuple arguments by recursive concatening them and unwrapping from Try at same time
template<typename... TailList>
static std::tuple<T...> unwrap(std::tuple<Try<T>...>&& t, TailList&&... tl)
{
return unwrap(std::move(t), std::forward<TailList>(tl)..., std::move(*std::get<sizeof...(TailList)>(t)));
}
// If that list is compatible with original T... , then we can finally make tuple from this list
static std::tuple<T...> unwrap(std::tuple<Try<T>...>&&, T&&... args)
{
return std::make_tuple(std::forward<T>(args)...);
}
};
}
template<typename... T> std::tuple<T...> unwrap_tuple(std::tuple<Try<T>...>&& ts)
{
return detail::UnwrapHelper<T...>::unwrap(std::forward<std::tuple<Try<T>...>>(ts));
}
}