#pragma once #include #include #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 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(&m_storage)->~T(); break; case State::Failure: reinterpret_cast(&m_storage)->~exception_ptr(); break; case State::None: break; } } explicit Try(const T& v) : m_state(State::Success) { static_assert(std::is_copy_constructible::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(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::value, "T must be copy constructible"); switch (m_state) { case State::Success: new(&m_storage) T(*reinterpret_cast(&src.m_storage)); break; case State::Failure: new(&m_storage) std::exception_ptr(*reinterpret_cast(&src.m_storage)); break; case State::None: break; } } Try& operator = (const Try& src) { static_assert(std::is_copy_constructible::value, "T must be copy constructible"); m_state = src.m_state; switch (m_state) { case State::Success: new(&m_storage) T(*reinterpret_cast(&src.m_storage)); break; case State::Failure: new(&m_storage) std::exception_ptr(*reinterpret_cast(&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(&src.m_storage))); break; case State::Failure: new(&m_storage) std::exception_ptr(std::move(*reinterpret_cast(&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(&src.m_storage))); break; case State::Failure: new(&m_storage) std::exception_ptr(std::move(*reinterpret_cast(&src.m_storage))); break; case State::None: break; } return *this; } T& value() & { throwIfFailure(); return *reinterpret_cast(&m_storage); } const T& value() const & { throwIfFailure(); return *reinterpret_cast(&m_storage); } T&& value() && { throwIfFailure(); return std::move(*reinterpret_cast(&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(&m_storage); } const std::exception_ptr& exception() const { return *reinterpret_cast(&m_storage); } bool isSuccess() const { return m_state == State::Success; } bool isFailure() const { return m_state == State::Failure; } template typename std::enable_if::type get() { return std::forward(*this); } template typename std::enable_if::type get() { return std::forward(value()); } template 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 { 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 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 R get() { return std::forward(*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 std::enable_if< !std::is_same::type, void>::value, Try::type>>::type make_try_with(F&& f) { using Result = typename std::result_of::type; try { return Try(f()); } catch (...) { return Try(std::current_exception()); } } template typename std::enable_if< std::is_same::type, void>::value, Try>::type make_try_with(F&& f) { try { f(); return Try(); } catch (...) { return Try(std::current_exception()); } } namespace detail { template struct UnwrapHelper { // Build list of tuple arguments by recursive concatening them and unwrapping from Try at same time template static std::tuple unwrap(std::tuple...>&& t, TailList&&... tl) { return unwrap(std::move(t), std::forward(tl)..., std::move(*std::get(t))); } // If that list is compatible with original T... , then we can finally make tuple from this list static std::tuple unwrap(std::tuple...>&&, T&&... args) { return std::make_tuple(std::forward(args)...); } }; } template std::tuple unwrap_tuple(std::tuple...>&& ts) { return detail::UnwrapHelper::unwrap(std::forward...>>(ts)); } }