|
|
- #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));
- }
- }
|