Index: source/ps/Future.h =================================================================== --- source/ps/Future.h +++ source/ps/Future.h @@ -48,15 +48,14 @@ * Holds all relevant data. */ template -class SharedState : public ResultHolder +class Receiver : public ResultHolder { static constexpr bool VoidResult = std::is_same_v; public: - SharedState(std::function&& func) : - ResultHolder{std::nullopt}, - m_Func(std::move(func)) + Receiver() : + ResultHolder{std::nullopt} {} - ~SharedState() + ~Receiver() { // For safety, wait on started task completion, but not on pending ones (auto-cancelled). if (!Cancel()) @@ -66,8 +65,8 @@ } } - SharedState(const SharedState&) = delete; - SharedState(SharedState&&) = delete; + Receiver(const Receiver&) = delete; + Receiver(Receiver&&) = delete; bool IsDoneOrCanceled() const { @@ -122,8 +121,17 @@ std::atomic m_Status = Status::PENDING; std::mutex m_Mutex; std::condition_variable m_ConditionVariable; +}; + +template +struct SharedState +{ + SharedState(Func&& func) : + m_Func{std::forward(func)} + {} - std::function m_Func; + Func m_Func{}; + Receiver> m_Recv{}; }; } // namespace FutureSharedStateDetail @@ -150,7 +158,6 @@ static constexpr bool VoidResult = std::is_same_v; using Status = FutureSharedStateDetail::Status; - using SharedState = FutureSharedStateDetail::SharedState; public: Future() = default; Future(const Future& o) = delete; @@ -161,8 +168,8 @@ /** * Make the future wait for the result of @a func. */ - template - PackagedTask Wrap(T&& func); + template + PackagedTask Wrap(Func&& func); /** * Move the result out of the future, and invalidate the future. @@ -223,7 +230,7 @@ } protected: - std::shared_ptr m_SharedState; + std::shared_ptr> m_SharedState; }; /** @@ -232,35 +239,39 @@ * This type is mostly just the shared state and the call operator, * handling the promise & continuation logic. */ -template +template class PackagedTask { - static constexpr bool VoidResult = std::is_same_v; public: PackagedTask() = delete; - PackagedTask(std::shared_ptr::SharedState> ss) : m_SharedState(std::move(ss)) {} + PackagedTask(std::shared_ptr> ss) : + m_SharedState(std::move(ss)) + {} void operator()() { - typename Future::Status expected = Future::Status::PENDING; - if (!m_SharedState->m_Status.compare_exchange_strong(expected, Future::Status::STARTED)) + FutureSharedStateDetail::Status expected = FutureSharedStateDetail::Status::PENDING; + if (!m_SharedState->m_Recv.m_Status.compare_exchange_strong(expected, + FutureSharedStateDetail::Status::STARTED)) + { return; + } - if constexpr (VoidResult) + if constexpr (std::is_void_v>) m_SharedState->m_Func(); else - m_SharedState->emplace(m_SharedState->m_Func()); + m_SharedState->m_Recv.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 // - receive the notification when we're done. // This requires locking the mutex (@see Wait). { - std::lock_guard lock(m_SharedState->m_Mutex); - m_SharedState->m_Status = Future::Status::DONE; + std::lock_guard lock(m_SharedState->m_Recv.m_Mutex); + m_SharedState->m_Recv.m_Status = FutureSharedStateDetail::Status::DONE; } - m_SharedState->m_ConditionVariable.notify_all(); + m_SharedState->m_Recv.m_ConditionVariable.notify_all(); // We no longer need the shared state, drop it immediately. m_SharedState.reset(); @@ -272,18 +283,19 @@ m_SharedState.reset(); } -protected: - std::shared_ptr::SharedState> m_SharedState; +private: + std::shared_ptr> m_SharedState; }; template -template -PackagedTask Future::Wrap(T&& func) +template +PackagedTask Future::Wrap(Func&& func) { - static_assert(std::is_same_v, ResultType>, + static_assert(std::is_same_v, ResultType>, "The return type of the wrapped function is not the same as the type of the Future."); - m_SharedState = std::make_shared(std::move(func)); - return PackagedTask(m_SharedState); + auto temp = std::make_shared>(std::move(func)); + m_SharedState = std::shared_ptr>{temp, &temp->m_Recv}; + return PackagedTask(std::move(temp)); } #endif // INCLUDED_FUTURE Index: source/ps/tests/test_Future.h =================================================================== --- source/ps/tests/test_Future.h +++ source/ps/tests/test_Future.h @@ -124,4 +124,21 @@ task2(); TS_ASSERT_EQUALS(future.Get(), 7); } + + void test_move_only_function() + { + Future future; + + class MoveOnlyType + { + public: + MoveOnlyType() = default; + MoveOnlyType(MoveOnlyType&) = delete; + MoveOnlyType& operator=(MoveOnlyType&) = delete; + MoveOnlyType(MoveOnlyType&&) = default; + MoveOnlyType& operator=(MoveOnlyType&&) = default; + }; + + future.Wrap([t = MoveOnlyType{}]{}); + } };