diff --git a/headers/promise.hpp/jobber.hpp b/headers/promise.hpp/jobber.hpp index 615c00a..58b0434 100644 --- a/headers/promise.hpp/jobber.hpp +++ b/headers/promise.hpp/jobber.hpp @@ -26,7 +26,7 @@ namespace jobber_hpp timeout }; - class jobber_cancelled_exception : public std::runtime_error { + class jobber_cancelled_exception final : public std::runtime_error { public: jobber_cancelled_exception() : std::runtime_error("jobber has stopped working") {} @@ -102,7 +102,7 @@ namespace jobber_hpp mutable std::condition_variable cond_var_; }; - class jobber::task : private noncopyable { + class jobber::task : private detail::noncopyable { public: virtual ~task() noexcept = default; virtual void run() noexcept = 0; @@ -110,7 +110,7 @@ namespace jobber_hpp }; template < typename R, typename F, typename... Args > - class jobber::concrete_task : public task { + class jobber::concrete_task final : public task { F f_; std::tuple args_; promise promise_; @@ -123,7 +123,7 @@ namespace jobber_hpp }; template < typename F, typename... Args > - class jobber::concrete_task : public task { + class jobber::concrete_task final : public task { F f_; std::tuple args_; promise promise_; diff --git a/headers/promise.hpp/promise.hpp b/headers/promise.hpp/promise.hpp index 4570a69..f22b737 100644 --- a/headers/promise.hpp/promise.hpp +++ b/headers/promise.hpp/promise.hpp @@ -78,107 +78,6 @@ namespace promise_hpp template < typename R, typename T > inline constexpr bool is_promise_r_v = is_promise_r::value; - // - // detail - // - - namespace detail - { - class noncopyable { - public: - noncopyable(const noncopyable&) = delete; - noncopyable& operator=(const noncopyable&) = delete; - protected: - noncopyable() = default; - ~noncopyable() = default; - }; - - template < typename T > - class storage final : private noncopyable { - public: - storage() = default; - - ~storage() - noexcept(std::is_nothrow_destructible_v) { - if ( initialized_ ) { - ptr_()->~T(); - } - } - - void set(T&& value) - noexcept(std::is_nothrow_move_constructible_v) { - assert(!initialized_); - ::new(ptr_()) T(std::move(value)); - initialized_ = true; - } - - void set(const T& value) - noexcept(std::is_nothrow_copy_constructible_v) { - assert(!initialized_); - ::new(ptr_()) T(value); - initialized_ = true; - } - - T get() - noexcept(std::is_nothrow_move_constructible_v) { - assert(initialized_); - return std::move(*ptr_()); - } - - T& value() noexcept { - assert(initialized_); - return *ptr_(); - } - - const T& value() const noexcept { - assert(initialized_); - return *ptr_(); - } - private: - T* ptr_() noexcept { - return reinterpret_cast(&data_); - } - - const T* ptr_() const noexcept { - return reinterpret_cast(&data_); - } - private: - std::aligned_storage_t data_; - bool initialized_ = false; - }; - - template < typename T > - class storage final : private noncopyable { - public: - storage() = default; - ~storage() = default; - - void set(T& value) noexcept { - assert(!initialized_); - value_ = &value; - initialized_ = true; - } - - T& get() noexcept { - assert(initialized_); - return *value_; - } - - T& value() noexcept { - assert(initialized_); - return *value_; - } - - const T& value() const noexcept { - assert(initialized_); - return *value_; - } - private: - T* value_{nullptr}; - bool initialized_ = false; - }; - } - // // promise_wait_status // @@ -189,6 +88,111 @@ namespace promise_hpp }; } +// ----------------------------------------------------------------------------- +// +// detail +// +// ----------------------------------------------------------------------------- + +namespace promise_hpp::detail +{ + template < typename T > + void destroy_in_place(T& ref) noexcept { + ref.~T(); + } + + template < typename T, typename... Args > + void construct_in_place(T& ref, Args&&... args) + noexcept(std::is_nothrow_constructible_v) { + ::new (std::addressof(ref)) T(std::forward(args)...); + } + + class noncopyable { + public: + noncopyable(const noncopyable&) = delete; + noncopyable& operator=(const noncopyable&) = delete; + protected: + noncopyable() = default; + ~noncopyable() = default; + }; + + template < typename T > + class storage final : private noncopyable { + public: + storage() = default; + + ~storage() noexcept { + if ( initialized_ ) { + destroy_in_place(*ptr_()); + } + } + + storage& operator=(T&& value) + noexcept(std::is_nothrow_move_constructible_v) { + assert(!initialized_); + construct_in_place(*ptr_(), std::move(value)); + initialized_ = true; + return *this; + } + + storage& operator=(const T& value) + noexcept(std::is_nothrow_copy_constructible_v) { + assert(!initialized_); + construct_in_place(*ptr_(), value); + initialized_ = true; + return *this; + } + + T& operator*() noexcept { + assert(initialized_); + return *ptr_(); + } + + const T& operator*() const noexcept { + assert(initialized_); + return *ptr_(); + } + private: + T* ptr_() noexcept { + return reinterpret_cast(&data_); + } + + const T* ptr_() const noexcept { + return reinterpret_cast(&data_); + } + private: + std::aligned_storage_t data_; + bool initialized_ = false; + }; + + template < typename T > + class storage final : private noncopyable { + public: + storage() = default; + ~storage() = default; + + storage& operator=(T& value) noexcept { + assert(!initialized_); + value_ = &value; + initialized_ = true; + return *this; + } + + T& operator*() noexcept { + assert(initialized_); + return *value_; + } + + const T& operator*() const noexcept { + assert(initialized_); + return *value_; + } + private: + T* value_{nullptr}; + bool initialized_ = false; + }; +} + // ----------------------------------------------------------------------------- // // promise @@ -299,10 +303,10 @@ namespace promise_hpp then([ n = next, f = std::forward(on_resolve) - ](auto&&... vs) mutable { + ](auto&& v) mutable { auto np = std::invoke( std::forward(f), - std::forward(vs)...); + std::forward(v)); std::move(np).then([n](auto&&... nvs) mutable { n.resolve(std::forward(nvs)...); }).except([n](std::exception_ptr e) mutable { @@ -417,7 +421,7 @@ namespace promise_hpp std::rethrow_exception(exception_); } assert(status_ == status::resolved); - return storage_.value(); + return *storage_; } void wait() const noexcept { @@ -449,7 +453,7 @@ namespace promise_hpp if ( status_ != status::pending ) { return false; } - storage_.set(std::forward(value)); + storage_ = std::forward(value); status_ = status::resolved; invoke_resolve_handlers_(); cond_var_.notify_all(); @@ -553,7 +557,7 @@ namespace promise_hpp if ( status_ == status::resolved ) { std::invoke( std::forward(resolve), - storage_.value()); + *storage_); } else if ( status_ == status::rejected ) { std::invoke( std::forward(reject), @@ -567,7 +571,7 @@ namespace promise_hpp void invoke_resolve_handlers_() noexcept { for ( const auto& h : handlers_ ) { - h.resolve_(storage_.value()); + h.resolve_(*storage_); } handlers_.clear(); } @@ -599,8 +603,8 @@ namespace promise_hpp reject_t reject_; }; - std::vector handlers_; detail::storage storage_; + std::vector handlers_; }; }; } @@ -713,10 +717,9 @@ namespace promise_hpp then([ n = next, f = std::forward(on_resolve) - ](auto&&... vs) mutable { + ]() mutable { auto np = std::invoke( - std::forward(f), - std::forward(vs)...); + std::forward(f)); std::move(np).then([n](auto&&... nvs) mutable { n.resolve(std::forward(nvs)...); }).except([n](std::exception_ptr e) mutable { @@ -1102,7 +1105,7 @@ namespace promise_hpp template < typename T > bool apply_result(std::size_t index, T&& value) { - results_[index].set(std::forward(value)); + results_[index] = std::forward(value); return ++counter_ == results_.size(); } @@ -1110,7 +1113,7 @@ namespace promise_hpp std::vector ret; ret.reserve(results_.size()); for ( auto&& v : results_ ) { - ret.push_back(v.get()); + ret.push_back(std::move(*v)); } return ret; } @@ -1205,11 +1208,11 @@ namespace promise_hpp using tuple_promise_result_t = typename tuple_promise_result::type; template < typename... ResultTypes > - class tuple_promise_context_t { + class tuple_promise_context_t final : private detail::noncopyable { public: template < std::size_t N, typename T > bool apply_result(T&& value) { - std::get(results_).set(std::forward(value)); + std::get(results_) = std::forward(value); return ++counter_ == sizeof...(ResultTypes); } @@ -1220,7 +1223,7 @@ namespace promise_hpp private: template < std::size_t... Is > std::tuple get_results_impl(std::index_sequence) { - return {std::get(results_).get()...}; + return {std::move(*std::get(results_))...}; } private: std::atomic_size_t counter_{0}; diff --git a/headers/promise.hpp/scheduler.hpp b/headers/promise.hpp/scheduler.hpp index 7ace422..d694ac6 100644 --- a/headers/promise.hpp/scheduler.hpp +++ b/headers/promise.hpp/scheduler.hpp @@ -26,7 +26,7 @@ namespace scheduler_hpp cancelled }; - class scheduler_cancelled_exception : public std::runtime_error { + class scheduler_cancelled_exception final : public std::runtime_error { public: scheduler_cancelled_exception() : std::runtime_error("scheduler has stopped working") {} @@ -82,7 +82,7 @@ namespace scheduler_hpp mutable std::condition_variable cond_var_; }; - class scheduler::task : private noncopyable { + class scheduler::task : private detail::noncopyable { public: virtual ~task() noexcept = default; virtual void run() noexcept = 0; @@ -90,7 +90,7 @@ namespace scheduler_hpp }; template < typename R, typename F, typename... Args > - class scheduler::concrete_task : public task { + class scheduler::concrete_task final : public task { F f_; std::tuple args_; promise promise_; @@ -103,7 +103,7 @@ namespace scheduler_hpp }; template < typename F, typename... Args > - class scheduler::concrete_task : public task { + class scheduler::concrete_task final : public task { F f_; std::tuple args_; promise promise_; diff --git a/untests/promise_tests.cpp b/untests/promise_tests.cpp index 59df20a..a112555 100644 --- a/untests/promise_tests.cpp +++ b/untests/promise_tests.cpp @@ -205,7 +205,7 @@ TEST_CASE("promise") { SECTION("resolved_ref") { { int* check_42_int = nullptr; - auto p = pr::promise(); + auto p = pr::promise>(); int i = 42; p.resolve(i); p.then([&check_42_int](int& value) mutable { @@ -216,7 +216,7 @@ TEST_CASE("promise") { } { const int* check_42_int = nullptr; - auto p = pr::promise(); + auto p = pr::promise>(); const int i = 42; p.resolve(i); p.then([&check_42_int](const int& value) mutable { @@ -892,8 +892,8 @@ TEST_CASE("promise") { }); } { - auto p1 = pr::promise(); - auto p2 = pr::promise(); + auto p1 = pr::promise>(); + auto p2 = pr::promise>(); auto p3 = pr::make_tuple_promise(std::make_tuple(p1, p2)); int i = 10;