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