Index: source/ps/Future.h =================================================================== --- source/ps/Future.h +++ source/ps/Future.h @@ -24,6 +24,7 @@ #include #include #include +#include #include template @@ -39,45 +40,22 @@ CANCELED }; -template -class SharedStateResult -{ -public: - void ResetResult() - { - if (m_HasResult) - m_Result.m_Result.~ResultType(); - m_HasResult = false; - } - - union Result - { - std::aligned_storage_t m_Bytes; - ResultType m_Result; - Result() : m_Bytes() {}; - ~Result() {}; - }; - // We don't use Result directly so the result doesn't have to be default constructible. - Result m_Result; - bool m_HasResult = false; -}; - -// Don't have m_Result for void ReturnType -template<> -class SharedStateResult -{ -}; +template +using ResultHolder = std::conditional_t, std::nullopt_t, std::optional>; /** * The shared state between futures and packaged state. * Holds all relevant data. */ template -class SharedState : public SharedStateResult +class SharedState : public ResultHolder { static constexpr bool VoidResult = std::is_same_v; public: - SharedState(std::function&& func) : m_Func(std::move(func)) {} + SharedState(std::function&& func) : + ResultHolder{std::nullopt}, + m_Func(std::move(func)) + {} ~SharedState() { // For safety, wait on started task completion, but not on pending ones (auto-cancelled). @@ -86,8 +64,6 @@ Wait(); Cancel(); } - if constexpr (!VoidResult) - SharedStateResult::ResetResult(); } SharedState(const SharedState&) = delete; @@ -122,7 +98,7 @@ if (m_Status == Status::DONE) m_Status = Status::CANCELED; if constexpr (!VoidResult) - SharedStateResult::ResetResult(); + ResultHolder::reset(); m_ConditionVariable.notify_all(); return cancelled; } @@ -136,10 +112,11 @@ std::enable_if_t, ResultType> GetResult() { // The caller must ensure that this is only called if we have a result. - ENSURE(SharedStateResult::m_HasResult); + ENSURE(ResultHolder::has_value()); m_Status = Status::CANCELED; - SharedStateResult::m_HasResult = false; - return std::move(SharedStateResult::m_Result.m_Result); + ResultType ret = std::move(ResultHolder::value()); + ResultHolder::reset(); + return ret; } std::atomic m_Status = Status::PENDING; @@ -283,11 +260,7 @@ if constexpr (VoidResult) m_SharedState->m_Func(); else - { - // To avoid UB, explicitly placement-new the value. - new (&m_SharedState->m_Result) ResultType{std::move(m_SharedState->m_Func())}; - m_SharedState->m_HasResult = true; - } + m_SharedState->emplace(m_SharedState->m_Func()); // Because we might have threads waiting on us, we need to make sure that they either: // - don't wait on our condition variable