C++17 refactoring, reference support

This commit is contained in:
BlackMATov
2019-12-30 03:33:06 +07:00
parent 7fbac493ad
commit b6f5f36dc4
3 changed files with 390 additions and 340 deletions

View File

@@ -10,14 +10,14 @@
[badge.travis]: https://img.shields.io/travis/BlackMATov/promise.hpp/master.svg?logo=travis [badge.travis]: https://img.shields.io/travis/BlackMATov/promise.hpp/master.svg?logo=travis
[badge.appveyor]: https://img.shields.io/appveyor/ci/BlackMATov/promise-hpp/master.svg?logo=appveyor [badge.appveyor]: https://img.shields.io/appveyor/ci/BlackMATov/promise-hpp/master.svg?logo=appveyor
[badge.codecov]: https://img.shields.io/codecov/c/github/BlackMATov/promise.hpp/master.svg?logo=codecov [badge.codecov]: https://img.shields.io/codecov/c/github/BlackMATov/promise.hpp/master.svg?logo=codecov
[badge.language]: https://img.shields.io/badge/language-C%2B%2B14-red.svg [badge.language]: https://img.shields.io/badge/language-C%2B%2B17-yellow.svg
[badge.license]: https://img.shields.io/badge/license-MIT-blue.svg [badge.license]: https://img.shields.io/badge/license-MIT-blue.svg
[badge.paypal]: https://img.shields.io/badge/donate-PayPal-orange.svg?logo=paypal&colorA=00457C [badge.paypal]: https://img.shields.io/badge/donate-PayPal-orange.svg?logo=paypal&colorA=00457C
[travis]: https://travis-ci.org/BlackMATov/promise.hpp [travis]: https://travis-ci.org/BlackMATov/promise.hpp
[appveyor]: https://ci.appveyor.com/project/BlackMATov/promise-hpp [appveyor]: https://ci.appveyor.com/project/BlackMATov/promise-hpp
[codecov]: https://codecov.io/gh/BlackMATov/promise.hpp [codecov]: https://codecov.io/gh/BlackMATov/promise.hpp
[language]: https://en.wikipedia.org/wiki/C%2B%2B14 [language]: https://en.wikipedia.org/wiki/C%2B%2B17
[license]: https://en.wikipedia.org/wiki/MIT_License [license]: https://en.wikipedia.org/wiki/MIT_License
[paypal]: https://www.paypal.me/matov [paypal]: https://www.paypal.me/matov

View File

@@ -53,6 +53,9 @@ namespace promise_hpp
struct is_promise struct is_promise
: impl::is_promise_impl<std::remove_cv_t<T>> {}; : impl::is_promise_impl<std::remove_cv_t<T>> {};
template < typename T >
inline constexpr bool is_promise_v = is_promise<T>::value;
// //
// is_promise_r // is_promise_r
// //
@@ -72,6 +75,9 @@ namespace promise_hpp
struct is_promise_r struct is_promise_r
: impl::is_promise_r_impl<R, std::remove_cv_t<T>> {}; : impl::is_promise_r_impl<R, std::remove_cv_t<T>> {};
template < typename R, typename T >
inline constexpr bool is_promise_r_v = is_promise_r<R, T>::value;
// //
// detail // detail
// //
@@ -93,29 +99,37 @@ namespace promise_hpp
storage() = default; storage() = default;
~storage() ~storage()
noexcept(std::is_nothrow_destructible<T>::value) noexcept(std::is_nothrow_destructible_v<T>) {
{
if ( initialized_ ) { if ( initialized_ ) {
ptr_()->~T(); ptr_()->~T();
} }
} }
template < typename U > void set(T&& value)
void set(U&& value) noexcept(std::is_nothrow_move_constructible_v<T>) {
noexcept(std::is_nothrow_constructible<T,U&&>::value)
{
assert(!initialized_); assert(!initialized_);
::new(ptr_()) T(std::forward<U>(value)); ::new(ptr_()) T(std::move(value));
initialized_ = true;
}
void set(const T& value)
noexcept(std::is_nothrow_copy_constructible_v<T>) {
assert(!initialized_);
::new(ptr_()) T(value);
initialized_ = true; initialized_ = true;
} }
T get() T get()
noexcept(std::is_nothrow_move_constructible<T>::value) noexcept(std::is_nothrow_move_constructible_v<T>) {
{
assert(initialized_); assert(initialized_);
return std::move(*ptr_()); return std::move(*ptr_());
} }
T& value() noexcept {
assert(initialized_);
return *ptr_();
}
const T& value() const noexcept { const T& value() const noexcept {
assert(initialized_); assert(initialized_);
return *ptr_(); return *ptr_();
@@ -132,6 +146,37 @@ namespace promise_hpp
std::aligned_storage_t<sizeof(T), alignof(T)> data_; std::aligned_storage_t<sizeof(T), alignof(T)> data_;
bool initialized_ = false; bool initialized_ = false;
}; };
template < typename T >
class storage<T&> 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;
};
} }
// //
@@ -142,21 +187,29 @@ namespace promise_hpp
no_timeout, no_timeout,
timeout timeout
}; };
}
// -----------------------------------------------------------------------------
// //
// promise<T> // promise<T>
// //
// -----------------------------------------------------------------------------
namespace promise_hpp
{
template < typename T > template < typename T >
class promise final { class promise final {
public: public:
using value_type = T; using value_type = T;
public:
promise() promise()
: state_(std::make_shared<state>()) {} : state_(std::make_shared<state>()) {}
promise(const promise&) noexcept = default; promise(promise&&) = default;
promise& operator=(const promise&) noexcept = default; promise& operator=(promise&&) = default;
promise(const promise&) = default;
promise& operator=(const promise&) = default;
void swap(promise& other) noexcept { void swap(promise& other) noexcept {
state_.swap(other.state_); state_.swap(other.state_);
@@ -178,50 +231,80 @@ namespace promise_hpp
return l.state_ != r.state_; return l.state_ != r.state_;
} }
template < typename ResolveF //
, typename ResolveFR = std::invoke_result_t<ResolveF,T> > // get
std::enable_if_t< //
is_promise<ResolveFR>::value && std::is_void<typename ResolveFR::value_type>::value,
promise<typename ResolveFR::value_type>>
then(ResolveF&& on_resolve) {
promise<typename ResolveFR::value_type> next;
then([ const T& get() const {
n = next, return state_->get();
f = std::forward<ResolveF>(on_resolve)
](auto&& v) mutable {
auto np = std::invoke(
std::forward<decltype(f)>(f),
std::forward<decltype(v)>(v));
std::move(np).then([n]() mutable {
n.resolve();
}).except([n](std::exception_ptr e) mutable {
n.reject(e);
});
}).except([n = next](std::exception_ptr e) mutable {
n.reject(e);
});
return next;
} }
template < typename U >
T get_or_default(U&& def) const {
try {
return get();
} catch (...) {
return std::forward<U>(def);
}
}
//
// wait
//
void wait() const noexcept {
state_->wait();
}
template < typename Rep, typename Period >
promise_wait_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
return state_->wait_for(timeout_duration);
}
template < typename Clock, typename Duration >
promise_wait_status wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) const {
return state_->wait_until(timeout_time);
}
//
// resolve/reject
//
template < typename U >
bool resolve(U&& value) {
return state_->resolve(std::forward<U>(value));
}
bool reject(std::exception_ptr e) noexcept {
return state_->reject(e);
}
template < typename E >
bool reject(E&& e) {
return state_->reject(std::make_exception_ptr(std::forward<E>(e)));
}
//
// then
//
template < typename ResolveF template < typename ResolveF
, typename ResolveFR = std::invoke_result_t<ResolveF,T> > , typename ResolveR = std::invoke_result_t<ResolveF, T> >
std::enable_if_t< std::enable_if_t<
is_promise<ResolveFR>::value && !std::is_void<typename ResolveFR::value_type>::value, is_promise_v<ResolveR>,
promise<typename ResolveFR::value_type>> promise<typename ResolveR::value_type>>
then(ResolveF&& on_resolve) { then(ResolveF&& on_resolve) {
promise<typename ResolveFR::value_type> next; promise<typename ResolveR::value_type> next;
then([ then([
n = next, n = next,
f = std::forward<ResolveF>(on_resolve) f = std::forward<ResolveF>(on_resolve)
](auto&& v) mutable { ](auto&&... vs) mutable {
auto np = std::invoke( auto np = std::invoke(
std::forward<decltype(f)>(f), std::forward<decltype(f)>(f),
std::forward<decltype(v)>(v)); std::forward<decltype(vs)>(vs)...);
std::move(np).then([n](auto&& nv) mutable { std::move(np).then([n](auto&&... nvs) mutable {
n.resolve(std::forward<decltype(nv)>(nv)); n.resolve(std::forward<decltype(nvs)>(nvs)...);
}).except([n](std::exception_ptr e) mutable { }).except([n](std::exception_ptr e) mutable {
n.reject(e); n.reject(e);
}); });
@@ -268,38 +351,48 @@ namespace promise_hpp
}); });
} }
//
// then
//
template < typename ResolveF
, typename ResolveR = std::invoke_result_t<ResolveF, T> >
std::enable_if_t<
!is_promise_v<ResolveR>,
promise<ResolveR>>
then(ResolveF&& on_resolve) {
promise<ResolveR> next;
state_->attach(
next,
std::forward<ResolveF>(on_resolve),
[](std::exception_ptr e) -> ResolveR { std::rethrow_exception(e); },
false);
return next;
}
template < typename ResolveF template < typename ResolveF
, typename RejectF , typename RejectF
, typename ResolveFR = std::invoke_result_t<ResolveF,T> > , typename ResolveR = std::invoke_result_t<ResolveF, T> >
std::enable_if_t< std::enable_if_t<
!is_promise<ResolveFR>::value, !is_promise_v<ResolveR>,
promise<ResolveFR>> promise<ResolveR>>
then(ResolveF&& on_resolve, RejectF&& on_reject) { then(ResolveF&& on_resolve, RejectF&& on_reject) {
promise<ResolveFR> next; promise<ResolveR> next;
state_->attach( state_->attach(
next, next,
std::forward<ResolveF>(on_resolve), std::forward<ResolveF>(on_resolve),
std::forward<RejectF>(on_reject), std::forward<RejectF>(on_reject),
true); true);
return next; return next;
} }
template < typename ResolveF //
, typename ResolveFR = std::invoke_result_t<ResolveF,T> > // except
std::enable_if_t< //
!is_promise<ResolveFR>::value,
promise<ResolveFR>>
then(ResolveF&& on_resolve) {
promise<ResolveFR> next;
state_->attach(
next,
std::forward<ResolveF>(on_resolve),
[](std::exception_ptr e) -> ResolveFR {
std::rethrow_exception(e);
},
false);
return next;
}
template < typename RejectF > template < typename RejectF >
promise<T> except(RejectF&& on_reject) { promise<T> except(RejectF&& on_reject) {
@@ -307,52 +400,6 @@ namespace promise_hpp
[](auto&& v) { return std::forward<decltype(v)>(v); }, [](auto&& v) { return std::forward<decltype(v)>(v); },
std::forward<RejectF>(on_reject)); std::forward<RejectF>(on_reject));
} }
template < typename U >
bool resolve(U&& value) {
return state_->resolve(std::forward<U>(value));
}
bool reject(std::exception_ptr e) noexcept {
return state_->reject(e);
}
template < typename E >
bool reject(E&& e) {
return state_->reject(
std::make_exception_ptr(std::forward<E>(e)));
}
const T& get() const {
return state_->get();
}
template < typename U >
T get_or_default(U&& def) const {
try {
return get();
} catch (...) {
return std::forward<U>(def);
}
}
void wait() const noexcept {
state_->wait();
}
template < typename Rep, typename Period >
promise_wait_status wait_for(
const std::chrono::duration<Rep, Period>& timeout_duration) const
{
return state_->wait_for(timeout_duration);
}
template < typename Clock, typename Duration >
promise_wait_status wait_until(
const std::chrono::time_point<Clock, Duration>& timeout_time) const
{
return state_->wait_until(timeout_time);
}
private: private:
class state; class state;
std::shared_ptr<state> state_; std::shared_ptr<state> state_;
@@ -361,33 +408,8 @@ namespace promise_hpp
public: public:
state() = default; state() = default;
template < typename U >
bool resolve(U&& value) {
std::lock_guard<std::mutex> guard(mutex_);
if ( status_ != status::pending ) {
return false;
}
storage_.set(std::forward<U>(value));
status_ = status::resolved;
invoke_resolve_handlers_();
cond_var_.notify_all();
return true;
}
bool reject(std::exception_ptr e) noexcept {
std::lock_guard<std::mutex> guard(mutex_);
if ( status_ != status::pending ) {
return false;
}
exception_ = e;
status_ = status::rejected;
invoke_reject_handlers_();
cond_var_.notify_all();
return true;
}
const T& get() { const T& get() {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock lock(mutex_);
cond_var_.wait(lock, [this](){ cond_var_.wait(lock, [this](){
return status_ != status::pending; return status_ != status::pending;
}); });
@@ -399,42 +421,56 @@ namespace promise_hpp
} }
void wait() const noexcept { void wait() const noexcept {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock lock(mutex_);
cond_var_.wait(lock, [this](){ cond_var_.wait(lock, [this](){
return status_ != status::pending; return status_ != status::pending;
}); });
} }
template < typename Rep, typename Period > template < typename Rep, typename Period >
promise_wait_status wait_for( promise_wait_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
const std::chrono::duration<Rep, Period>& timeout_duration) const std::unique_lock lock(mutex_);
{
std::unique_lock<std::mutex> lock(mutex_);
return cond_var_.wait_for(lock, timeout_duration, [this](){ return cond_var_.wait_for(lock, timeout_duration, [this](){
return status_ != status::pending; return status_ != status::pending;
}) ? promise_wait_status::no_timeout }) ? promise_wait_status::no_timeout : promise_wait_status::timeout;
: promise_wait_status::timeout;
} }
template < typename Clock, typename Duration > template < typename Clock, typename Duration >
promise_wait_status wait_until( promise_wait_status wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) const {
const std::chrono::time_point<Clock, Duration>& timeout_time) const std::unique_lock lock(mutex_);
{
std::unique_lock<std::mutex> lock(mutex_);
return cond_var_.wait_until(lock, timeout_time, [this](){ return cond_var_.wait_until(lock, timeout_time, [this](){
return status_ != status::pending; return status_ != status::pending;
}) ? promise_wait_status::no_timeout }) ? promise_wait_status::no_timeout : promise_wait_status::timeout;
: promise_wait_status::timeout;
} }
template < typename U >
bool resolve(U&& value) {
std::lock_guard guard(mutex_);
if ( status_ != status::pending ) {
return false;
}
storage_.set(std::forward<U>(value));
status_ = status::resolved;
invoke_resolve_handlers_();
cond_var_.notify_all();
return true;
}
bool reject(std::exception_ptr e) noexcept {
std::lock_guard guard(mutex_);
if ( status_ != status::pending ) {
return false;
}
exception_ = e;
status_ = status::rejected;
invoke_reject_handlers_();
cond_var_.notify_all();
return true;
}
public:
template < typename U, typename ResolveF, typename RejectF > template < typename U, typename ResolveF, typename RejectF >
std::enable_if_t<std::is_void<U>::value, void> std::enable_if_t<std::is_void<U>::value, void>
attach( attach(promise<U>& next, ResolveF&& on_resolve, RejectF&& on_reject, bool has_reject) {
promise<U>& next,
ResolveF&& on_resolve,
RejectF&& on_reject,
bool has_reject)
{
auto reject_h = [ auto reject_h = [
n = next, n = next,
f = std::forward<RejectF>(on_reject), f = std::forward<RejectF>(on_reject),
@@ -468,18 +504,13 @@ namespace promise_hpp
} }
}; };
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard guard(mutex_);
add_handlers_(std::move(resolve_h), std::move(reject_h)); add_handlers_(std::move(resolve_h), std::move(reject_h));
} }
template < typename U, typename ResolveF, typename RejectF > template < typename U, typename ResolveF, typename RejectF >
std::enable_if_t<!std::is_void<U>::value, void> std::enable_if_t<!std::is_void<U>::value, void>
attach( attach(promise<U>& next, ResolveF&& on_resolve, RejectF&& on_reject, bool has_reject) {
promise<U>& next,
ResolveF&& on_resolve,
RejectF&& on_reject,
bool has_reject)
{
auto reject_h = [ auto reject_h = [
n = next, n = next,
f = std::forward<RejectF>(on_reject), f = std::forward<RejectF>(on_reject),
@@ -513,7 +544,7 @@ namespace promise_hpp
} }
}; };
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard guard(mutex_);
add_handlers_(std::move(resolve_h), std::move(reject_h)); add_handlers_(std::move(resolve_h), std::move(reject_h));
} }
private: private:
@@ -528,16 +559,15 @@ namespace promise_hpp
std::forward<RejectF>(reject), std::forward<RejectF>(reject),
exception_); exception_);
} else { } else {
handlers_.emplace_back( handlers_.push_back({
std::forward<ResolveF>(resolve), std::forward<ResolveF>(resolve),
std::forward<RejectF>(reject)); std::forward<RejectF>(reject)});
} }
} }
void invoke_resolve_handlers_() noexcept { void invoke_resolve_handlers_() noexcept {
const T& value = storage_.value();
for ( const auto& h : handlers_ ) { for ( const auto& h : handlers_ ) {
h.resolve_(value); h.resolve_(storage_.value());
} }
handlers_.clear(); handlers_.clear();
} }
@@ -567,32 +597,35 @@ namespace promise_hpp
resolve_t resolve_; resolve_t resolve_;
reject_t reject_; reject_t reject_;
template < typename ResolveF, typename RejectF >
handler(ResolveF&& resolve, RejectF&& reject)
: resolve_(std::forward<ResolveF>(resolve))
, reject_(std::forward<RejectF>(reject)) {}
}; };
std::vector<handler> handlers_; std::vector<handler> handlers_;
detail::storage<T> storage_; detail::storage<T> storage_;
}; };
}; };
}
// -----------------------------------------------------------------------------
// //
// promise<void> // promise<void>
// //
// -----------------------------------------------------------------------------
namespace promise_hpp
{
template <> template <>
class promise<void> final { class promise<void> final {
public: public:
using value_type = void; using value_type = void;
public:
promise() promise()
: state_(std::make_shared<state>()) {} : state_(std::make_shared<state>()) {}
promise(const promise&) noexcept = default; promise(promise&&) = default;
promise& operator=(const promise&) noexcept = default; promise& operator=(promise&&) = default;
promise(const promise&) = default;
promise& operator=(const promise&) = default;
void swap(promise& other) noexcept { void swap(promise& other) noexcept {
state_.swap(other.state_); state_.swap(other.state_);
@@ -614,48 +647,78 @@ namespace promise_hpp
return l.state_ != r.state_; return l.state_ != r.state_;
} }
template < typename ResolveF //
, typename ResolveFR = std::invoke_result_t<ResolveF> > // get
std::enable_if_t< //
is_promise<ResolveFR>::value && std::is_void<typename ResolveFR::value_type>::value,
promise<typename ResolveFR::value_type>>
then(ResolveF&& on_resolve) {
promise<typename ResolveFR::value_type> next;
then([ void get() const {
n = next, state_->get();
f = std::forward<ResolveF>(on_resolve)
]() mutable {
auto np = std::invoke(
std::forward<decltype(f)>(f));
std::move(np).then([n]() mutable {
n.resolve();
}).except([n](std::exception_ptr e) mutable {
n.reject(e);
});
}).except([n = next](std::exception_ptr e) mutable {
n.reject(e);
});
return next;
} }
void get_or_default() const {
try {
return get();
} catch (...) {
// nothing
}
}
//
// wait
//
void wait() const noexcept {
state_->wait();
}
template < typename Rep, typename Period >
promise_wait_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
return state_->wait_for(timeout_duration);
}
template < typename Clock, typename Duration >
promise_wait_status wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) const {
return state_->wait_until(timeout_time);
}
//
// resolve/reject
//
bool resolve() {
return state_->resolve();
}
bool reject(std::exception_ptr e) noexcept {
return state_->reject(e);
}
template < typename E >
bool reject(E&& e) {
return state_->reject(std::make_exception_ptr(std::forward<E>(e)));
}
//
// then
//
template < typename ResolveF template < typename ResolveF
, typename ResolveFR = std::invoke_result_t<ResolveF> > , typename ResolveR = std::invoke_result_t<ResolveF> >
std::enable_if_t< std::enable_if_t<
is_promise<ResolveFR>::value && !std::is_void<typename ResolveFR::value_type>::value, is_promise_v<ResolveR>,
promise<typename ResolveFR::value_type>> promise<typename ResolveR::value_type>>
then(ResolveF&& on_resolve) { then(ResolveF&& on_resolve) {
promise<typename ResolveFR::value_type> next; promise<typename ResolveR::value_type> next;
then([ then([
n = next, n = next,
f = std::forward<ResolveF>(on_resolve) f = std::forward<ResolveF>(on_resolve)
]() mutable { ](auto&&... vs) mutable {
auto np = std::invoke( auto np = std::invoke(
std::forward<decltype(f)>(f)); std::forward<decltype(f)>(f),
std::move(np).then([n](auto&& nv) mutable { std::forward<decltype(vs)>(vs)...);
n.resolve(std::forward<decltype(nv)>(nv)); std::move(np).then([n](auto&&... nvs) mutable {
n.resolve(std::forward<decltype(nvs)>(nvs)...);
}).except([n](std::exception_ptr e) mutable { }).except([n](std::exception_ptr e) mutable {
n.reject(e); n.reject(e);
}); });
@@ -699,89 +762,55 @@ namespace promise_hpp
}); });
} }
//
// then
//
template < typename ResolveF
, typename ResolveR = std::invoke_result_t<ResolveF> >
std::enable_if_t<
!is_promise_v<ResolveR>,
promise<ResolveR>>
then(ResolveF&& on_resolve) {
promise<ResolveR> next;
state_->attach(
next,
std::forward<ResolveF>(on_resolve),
[](std::exception_ptr e) -> ResolveR { std::rethrow_exception(e); },
false);
return next;
}
template < typename ResolveF template < typename ResolveF
, typename RejectF , typename RejectF
, typename ResolveFR = std::invoke_result_t<ResolveF> > , typename ResolveR = std::invoke_result_t<ResolveF> >
std::enable_if_t< std::enable_if_t<
!is_promise<ResolveFR>::value, !is_promise_v<ResolveR>,
promise<ResolveFR>> promise<ResolveR>>
then(ResolveF&& on_resolve, RejectF&& on_reject) { then(ResolveF&& on_resolve, RejectF&& on_reject) {
promise<ResolveFR> next; promise<ResolveR> next;
state_->attach( state_->attach(
next, next,
std::forward<ResolveF>(on_resolve), std::forward<ResolveF>(on_resolve),
std::forward<RejectF>(on_reject), std::forward<RejectF>(on_reject),
true); true);
return next; return next;
} }
template < typename ResolveF //
, typename ResolveFR = std::invoke_result_t<ResolveF> > // except
std::enable_if_t< //
!is_promise<ResolveFR>::value,
promise<ResolveFR>>
then(ResolveF&& on_resolve) {
promise<ResolveFR> next;
state_->attach(
next,
std::forward<ResolveF>(on_resolve),
[](std::exception_ptr e) -> ResolveFR {
std::rethrow_exception(e);
},
false);
return next;
}
template < typename RejectF > template < typename RejectF >
promise<void> except(RejectF&& on_reject) { promise<void> except(RejectF&& on_reject) {
return then( return then(
[]{}, [](){},
std::forward<RejectF>(on_reject)); std::forward<RejectF>(on_reject));
} }
bool resolve() {
return state_->resolve();
}
bool reject(std::exception_ptr e) noexcept {
return state_->reject(e);
}
template < typename E >
bool reject(E&& e) {
return state_->reject(
std::make_exception_ptr(std::forward<E>(e)));
}
void get() const {
state_->get();
}
void get_or_default() const {
try {
return get();
} catch (...) {
// nothing
}
}
void wait() const noexcept {
state_->wait();
}
template < typename Rep, typename Period >
promise_wait_status wait_for(
const std::chrono::duration<Rep, Period>& timeout_duration) const
{
return state_->wait_for(timeout_duration);
}
template < typename Clock, typename Duration >
promise_wait_status wait_until(
const std::chrono::time_point<Clock, Duration>& timeout_time) const
{
return state_->wait_until(timeout_time);
}
private: private:
class state; class state;
std::shared_ptr<state> state_; std::shared_ptr<state> state_;
@@ -790,31 +819,8 @@ namespace promise_hpp
public: public:
state() = default; state() = default;
bool resolve() {
std::lock_guard<std::mutex> guard(mutex_);
if ( status_ != status::pending ) {
return false;
}
status_ = status::resolved;
invoke_resolve_handlers_();
cond_var_.notify_all();
return true;
}
bool reject(std::exception_ptr e) noexcept {
std::lock_guard<std::mutex> guard(mutex_);
if ( status_ != status::pending ) {
return false;
}
exception_ = e;
status_ = status::rejected;
invoke_reject_handlers_();
cond_var_.notify_all();
return true;
}
void get() { void get() {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock lock(mutex_);
cond_var_.wait(lock, [this](){ cond_var_.wait(lock, [this](){
return status_ != status::pending; return status_ != status::pending;
}); });
@@ -825,42 +831,54 @@ namespace promise_hpp
} }
void wait() const noexcept { void wait() const noexcept {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock lock(mutex_);
cond_var_.wait(lock, [this](){ cond_var_.wait(lock, [this](){
return status_ != status::pending; return status_ != status::pending;
}); });
} }
template < typename Rep, typename Period > template < typename Rep, typename Period >
promise_wait_status wait_for( promise_wait_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
const std::chrono::duration<Rep, Period>& timeout_duration) const std::unique_lock lock(mutex_);
{
std::unique_lock<std::mutex> lock(mutex_);
return cond_var_.wait_for(lock, timeout_duration, [this](){ return cond_var_.wait_for(lock, timeout_duration, [this](){
return status_ != status::pending; return status_ != status::pending;
}) ? promise_wait_status::no_timeout }) ? promise_wait_status::no_timeout : promise_wait_status::timeout;
: promise_wait_status::timeout;
} }
template < typename Clock, typename Duration > template < typename Clock, typename Duration >
promise_wait_status wait_until( promise_wait_status wait_until(const std::chrono::time_point<Clock, Duration>& timeout_time) const {
const std::chrono::time_point<Clock, Duration>& timeout_time) const std::unique_lock lock(mutex_);
{
std::unique_lock<std::mutex> lock(mutex_);
return cond_var_.wait_until(lock, timeout_time, [this](){ return cond_var_.wait_until(lock, timeout_time, [this](){
return status_ != status::pending; return status_ != status::pending;
}) ? promise_wait_status::no_timeout }) ? promise_wait_status::no_timeout : promise_wait_status::timeout;
: promise_wait_status::timeout;
} }
bool resolve() {
std::lock_guard guard(mutex_);
if ( status_ != status::pending ) {
return false;
}
status_ = status::resolved;
invoke_resolve_handlers_();
cond_var_.notify_all();
return true;
}
bool reject(std::exception_ptr e) noexcept {
std::lock_guard guard(mutex_);
if ( status_ != status::pending ) {
return false;
}
exception_ = e;
status_ = status::rejected;
invoke_reject_handlers_();
cond_var_.notify_all();
return true;
}
public:
template < typename U, typename ResolveF, typename RejectF > template < typename U, typename ResolveF, typename RejectF >
std::enable_if_t<std::is_void<U>::value, void> std::enable_if_t<std::is_void<U>::value, void>
attach( attach(promise<U>& next, ResolveF&& on_resolve, RejectF&& on_reject, bool has_reject) {
promise<U>& next,
ResolveF&& on_resolve,
RejectF&& on_reject,
bool has_reject)
{
auto reject_h = [ auto reject_h = [
n = next, n = next,
f = std::forward<RejectF>(on_reject), f = std::forward<RejectF>(on_reject),
@@ -893,18 +911,13 @@ namespace promise_hpp
} }
}; };
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard guard(mutex_);
add_handlers_(std::move(resolve_h), std::move(reject_h)); add_handlers_(std::move(resolve_h), std::move(reject_h));
} }
template < typename U, typename ResolveF, typename RejectF > template < typename U, typename ResolveF, typename RejectF >
std::enable_if_t<!std::is_void<U>::value, void> std::enable_if_t<!std::is_void<U>::value, void>
attach( attach(promise<U>& next, ResolveF&& on_resolve, RejectF&& on_reject, bool has_reject) {
promise<U>& next,
ResolveF&& on_resolve,
RejectF&& on_reject,
bool has_reject)
{
auto reject_h = [ auto reject_h = [
n = next, n = next,
f = std::forward<RejectF>(on_reject), f = std::forward<RejectF>(on_reject),
@@ -937,7 +950,7 @@ namespace promise_hpp
} }
}; };
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard guard(mutex_);
add_handlers_(std::move(resolve_h), std::move(reject_h)); add_handlers_(std::move(resolve_h), std::move(reject_h));
} }
private: private:
@@ -951,9 +964,9 @@ namespace promise_hpp
std::forward<RejectF>(reject), std::forward<RejectF>(reject),
exception_); exception_);
} else { } else {
handlers_.emplace_back( handlers_.push_back({
std::forward<ResolveF>(resolve), std::forward<ResolveF>(resolve),
std::forward<RejectF>(reject)); std::forward<RejectF>(reject)});
} }
} }
@@ -989,17 +1002,15 @@ namespace promise_hpp
resolve_t resolve_; resolve_t resolve_;
reject_t reject_; reject_t reject_;
template < typename ResolveF, typename RejectF >
handler(ResolveF&& resolve, RejectF&& reject)
: resolve_(std::forward<ResolveF>(resolve))
, reject_(std::forward<RejectF>(reject)) {}
}; };
std::vector<handler> handlers_; std::vector<handler> handlers_;
}; };
}; };
}
namespace promise_hpp
{
// //
// swap // swap
// //
@@ -1099,7 +1110,7 @@ namespace promise_hpp
std::vector<ResultType> ret; std::vector<ResultType> ret;
ret.reserve(results_.size()); ret.reserve(results_.size());
for ( auto&& v : results_ ) { for ( auto&& v : results_ ) {
ret.emplace_back(v.get()); ret.push_back(v.get());
} }
return ret; return ret;
} }
@@ -1209,7 +1220,7 @@ namespace promise_hpp
private: private:
template < std::size_t... Is > template < std::size_t... Is >
std::tuple<ResultTypes...> get_results_impl(std::index_sequence<Is...>) { std::tuple<ResultTypes...> get_results_impl(std::index_sequence<Is...>) {
return std::make_tuple(std::get<Is>(results_).get()...); return {std::get<Is>(results_).get()...};
} }
private: private:
std::atomic_size_t counter_{0}; std::atomic_size_t counter_{0};
@@ -1291,7 +1302,7 @@ namespace promise_hpp
make_tuple_promise(Tuple&& tuple) { make_tuple_promise(Tuple&& tuple) {
return impl::make_tuple_promise_impl( return impl::make_tuple_promise_impl(
std::forward<Tuple>(tuple), std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size<ResultTuple>::value>()); std::make_index_sequence<std::tuple_size_v<ResultTuple>>());
} }
} }

View File

@@ -202,6 +202,30 @@ TEST_CASE("promise") {
REQUIRE(check_100500_transform == 100500); REQUIRE(check_100500_transform == 100500);
} }
} }
SECTION("resolved_ref") {
{
int* check_42_int = nullptr;
auto p = pr::promise<int&>();
int i = 42;
p.resolve(i);
p.then([&check_42_int](int& value) mutable {
check_42_int = &value;
});
REQUIRE(check_42_int);
REQUIRE(*check_42_int == 42);
}
{
const int* check_42_int = nullptr;
auto p = pr::promise<const int&>();
const int i = 42;
p.resolve(i);
p.then([&check_42_int](const int& value) mutable {
check_42_int = &value;
});
REQUIRE(check_42_int);
REQUIRE(*check_42_int == 42);
}
}
SECTION("rejected") { SECTION("rejected") {
{ {
bool call_fail_with_logic_error = false; bool call_fail_with_logic_error = false;
@@ -867,6 +891,21 @@ TEST_CASE("promise") {
return std::make_tuple(std::move(p1), std::move(p2)); return std::make_tuple(std::move(p1), std::move(p2));
}); });
} }
{
auto p1 = pr::promise<int&>();
auto p2 = pr::promise<float&>();
auto p3 = pr::make_tuple_promise(std::make_tuple(p1, p2));
int i = 10;
float f = 0.f;
p1.resolve(i);
p2.resolve(f);
p3.then([&i,&f](const std::tuple<int&, float&>& t){
REQUIRE(&std::get<0>(t) == &i);
REQUIRE(&std::get<1>(t) == &f);
});
}
} }
SECTION("make_all_promise_fail") { SECTION("make_all_promise_fail") {
{ {