Promise creation from callback only (resolver)

Make sure that the promise state can only be changed by the promise producer (and not consumers) by removing the `fulfill` and `reject` methods from the instance members and introducing a new constructor accepting a resolver lambda. That also means that a promise can't anymore be default constructed.

Add the static `QPromise<T>::resolve` and `QPromise<T>::reject` methods to create synchronously fulfilled or rejected promises, and fix the `qPromise` helper to handle deduced promises (e.g. `qPromise(QFuture<int>()) -> QPromise<int>`).
This commit is contained in:
Simon Brunel
2017-05-20 09:40:42 +02:00
parent 6a642446df
commit ce3ed72dd4
9 changed files with 677 additions and 489 deletions

View File

@ -1,25 +1,21 @@
#ifndef _QTPROMISE_QPROMISE_H
#define _QTPROMISE_QPROMISE_H
// QPromise
// QtPromise
#include "qpromise_p.h"
#include "qpromiseglobal.h"
// Qt
#include <QExplicitlySharedDataPointer>
#include <QVariant>
namespace QtPromise {
using namespace QtPromisePrivate;
template <typename T>
class QPromiseBase
{
public:
using Type = T;
QPromiseBase() : m_d(new QPromiseData<T>()) { }
virtual ~QPromiseBase() { }
bool isFulfilled() const { return m_d->resolved && !m_d->rejected; }
@ -27,22 +23,34 @@ public:
bool isPending() const { return !m_d->resolved; }
template <typename TFulfilled, typename TRejected = std::nullptr_t>
inline auto then(TFulfilled fulfilled, TRejected rejected = nullptr)
-> typename QPromiseHandler<T, TFulfilled>::Promise;
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
then(TFulfilled fulfilled, TRejected rejected = nullptr);
template <typename TRejected>
inline auto fail(TRejected rejected)
-> typename QPromiseHandler<T, std::nullptr_t>::Promise;
template <typename TError>
inline QPromise<T> reject(const TError& error);
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
fail(TRejected rejected);
inline QPromise<T> wait() const;
protected:
QExplicitlySharedDataPointer<QPromiseData<T> > m_d;
public: // STATIC
template <typename E>
inline static QPromise<T> reject(const E& error);
virtual void notify(const typename QPromiseData<T>::HandlerList& handlers) const = 0;
protected:
friend class QPromiseResolve<T>;
friend class QPromiseReject<T>;
QExplicitlySharedDataPointer<QtPromisePrivate::PromiseData<T> > m_d;
inline QPromiseBase();
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type = 0>
inline QPromiseBase(F resolver);
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type = 0>
inline QPromiseBase(F resolver);
virtual void notify(const typename QtPromisePrivate::PromiseData<T>::HandlerList& handlers) const = 0;
inline void dispatch();
};
@ -51,42 +59,41 @@ template <typename T>
class QPromise: public QPromiseBase<T>
{
public:
QPromise() : QPromiseBase<T>() {}
template <typename F>
QPromise(F resolver): QPromiseBase<T>(resolver) { }
template <typename THandler>
inline QPromise<T> finally(THandler handler);
inline QPromise<T> fulfill(const T& value);
public: // STATIC
inline static QPromise<QVector<T> > all(const QVector<QPromise<T> >& promises);
inline static QPromise<T> resolve(const T& value);
protected:
inline void notify(const typename QPromiseData<T>::HandlerList& handlers) const Q_DECL_OVERRIDE;
inline void notify(const typename QtPromisePrivate::PromiseData<T>::HandlerList& handlers) const Q_DECL_OVERRIDE;
private:
friend class QPromiseBase<T>;
QPromise(const QPromiseBase<T>& p) : QPromiseBase<T>(p) { }
};
template <>
class QPromise<void>: public QPromiseBase<void>
{
public:
QPromise(): QPromiseBase<void>() {}
template <typename F>
QPromise(F resolver): QPromiseBase<void>(resolver) { }
template <typename THandler>
inline QPromise<void> finally(THandler handler);
inline QPromise<void> fulfill();
public: // STATIC
inline static QPromise<void> all(const QVector<QPromise<void> >& promises);
inline static QPromise<void> resolve();
protected:
inline void notify(const typename QPromiseData<void>::HandlerList& handlers) const Q_DECL_OVERRIDE;
inline void notify(const typename QtPromisePrivate::PromiseData<void>::HandlerList& handlers) const Q_DECL_OVERRIDE;
private:
friend class QPromiseBase<void>;
@ -94,13 +101,8 @@ private:
QPromise(const QPromiseBase<void>& p) : QPromiseBase<void>(p) { }
};
using QVariantPromise = QtPromise::QPromise<QVariant>;
} // namespace QtPromise
Q_DECLARE_TYPEINFO(QtPromise::QVariantPromise, Q_MOVABLE_TYPE);
Q_DECLARE_METATYPE(QtPromise::QVariantPromise)
#include "qpromise.inl"
#include "qpromise_qfuture.inl"

View File

@ -4,15 +4,91 @@
namespace QtPromise {
template <class T = void>
class QPromiseResolve
{
public:
QPromiseResolve(const QPromise<T>& p)
: m_promise(p)
{ }
void operator()(const T& value) const
{
auto p = m_promise;
if (p.isPending()) {
p.m_d->rejected = false;
p.m_d->resolved = true;
p.m_d->value = value;
p.dispatch();
}
}
private:
QPromise<T> m_promise;
};
template <>
class QPromiseResolve<void>
{
public:
QPromiseResolve(const QPromise<void>& p)
: m_promise(p)
{ }
void operator()() const
{
auto p = m_promise;
if (p.isPending()) {
p.m_d->rejected = false;
p.m_d->resolved = true;
p.dispatch();
}
}
private:
QPromise<void> m_promise;
};
template <class T = void>
class QPromiseReject
{
public:
QPromiseReject()
{ }
QPromiseReject(const QPromise<T>& p)
: m_promise(p)
{ }
void operator()(const QPromiseError& error) const
{
auto p = m_promise;
if (p.isPending()) {
p.m_d->rejected = true;
p.m_d->resolved = true;
p.m_d->error = error;
p.dispatch();
}
}
private:
QPromise<T> m_promise;
};
template <typename T>
template <typename TFulfilled, typename TRejected>
inline auto QPromiseBase<T>::then(TFulfilled fulfilled, TRejected rejected)
-> typename QPromiseHandler<T, TFulfilled>::Promise
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
QPromiseBase<T>::then(TFulfilled fulfilled, TRejected rejected)
{
typename QPromiseHandler<T, TFulfilled>::Promise next;
using namespace QtPromisePrivate;
using PromiseType = typename PromiseHandler<T, TFulfilled>::Promise;
m_d->handlers << QPromiseHandler<T, TFulfilled>::create(next, fulfilled);
m_d->catchers << QPromiseCatcher<T, TRejected>::create(next, rejected);
PromiseType next([=](
const QPromiseResolve<typename PromiseType::Type>& resolve,
const QPromiseReject<typename PromiseType::Type>& reject) {
m_d->handlers << PromiseHandler<T, TFulfilled>::create(fulfilled, resolve, reject);
m_d->catchers << PromiseCatcher<T, TRejected>::create(rejected, resolve, reject);
});
if (m_d->resolved) {
dispatch();
@ -23,26 +99,12 @@ inline auto QPromiseBase<T>::then(TFulfilled fulfilled, TRejected rejected)
template <typename T>
template <typename TRejected>
inline auto QPromiseBase<T>::fail(TRejected rejected)
-> typename QPromiseHandler<T, std::nullptr_t>::Promise
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
QPromiseBase<T>::fail(TRejected rejected)
{
return then(nullptr, rejected);
}
template <typename T>
template <typename TError>
inline QPromise<T> QPromiseBase<T>::reject(const TError& error)
{
if (!m_d->resolved) {
m_d->error = QtPromisePrivate::to_exception_ptr(error);
m_d->rejected = true;
m_d->resolved = true;
dispatch();
}
return *this;
}
template <typename T>
inline QPromise<T> QPromiseBase<T>::wait() const
{
@ -55,20 +117,67 @@ inline QPromise<T> QPromiseBase<T>::wait() const
return *this;
}
template <typename T>
template <typename E>
inline QPromise<T> QPromiseBase<T>::reject(const E& error)
{
return QPromise<T>([=](const QPromiseResolve<T>&) {
throw error;
});
}
template <typename T>
inline QPromiseBase<T>::QPromiseBase()
: m_d(new QtPromisePrivate::PromiseData<T>())
{
}
template <typename T>
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type>
inline QPromiseBase<T>::QPromiseBase(F resolver)
: m_d(new QtPromisePrivate::PromiseData<T>())
{
auto resolve = QPromiseResolve<T>(*this);
auto reject = QPromiseReject<T>(*this);
try {
resolver(resolve);
} catch(...) {
reject(std::current_exception());
}
}
template <typename T>
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type>
inline QPromiseBase<T>::QPromiseBase(F resolver)
: m_d(new QtPromisePrivate::PromiseData<T>())
{
auto resolve = QPromiseResolve<T>(*this);
auto reject = QPromiseReject<T>(*this);
try {
resolver(resolve, reject);
} catch(...) {
reject(std::current_exception());
}
}
template <typename T>
inline void QPromiseBase<T>::dispatch()
{
using namespace QtPromisePrivate;
Q_ASSERT(m_d->resolved);
typename QPromiseData<T>::HandlerList handlers;
typename QPromiseData<T>::CatcherList catchers;
typename PromiseData<T>::HandlerList handlers;
typename PromiseData<T>::CatcherList catchers;
handlers.swap(m_d->handlers);
catchers.swap(m_d->catchers);
if (m_d->rejected) {
const std::exception_ptr error = m_d->error;
qtpromise_defer([catchers, error]() {
const QPromiseError error = m_d->error;
qtpromise_defer([=]() {
for (auto catcher: catchers) {
catcher(error);
}
@ -83,67 +192,62 @@ template <typename THandler>
inline QPromise<T> QPromise<T>::finally(THandler handler)
{
return this->then([=](const T& res) {
return QPromise<void>().fulfill().then(handler).then([=](){
return QPromise<void>::resolve().then(handler).then([=](){
return res;
});
}, [=]() {
const auto exception = std::current_exception();
return QPromise<void>().fulfill().then(handler).then([=](){
return QPromise<void>::resolve().then(handler).then([=](){
std::rethrow_exception(exception);
return T();
});
});
}
template <typename T>
inline QPromise<T> QPromise<T>::fulfill(const T& value)
{
if (!this->m_d->resolved) {
this->m_d->rejected = false;
this->m_d->resolved = true;
this->m_d->value = value;
this->dispatch();
}
return *this;
}
template <typename T>
inline QPromise<QVector<T> > QPromise<T>::all(const QVector<QPromise<T> >& promises)
{
QPromise<QVector<T> > next;
const int count = promises.size();
if (count == 0) {
return next.fulfill({});
return QPromise<QVector<T> >::resolve({});
}
QSharedPointer<int> remaining(new int(count));
QSharedPointer<QVector<T> > results(new QVector<T>(count));
return QPromise<QVector<T> >([=](
const QPromiseResolve<QVector<T> >& resolve,
const QPromiseReject<QVector<T> >& reject) {
for (int i=0; i<count; ++i) {
QPromise<T>(promises[i]).then([=](const T& res) mutable {
if (next.isPending()) {
QSharedPointer<int> remaining(new int(count));
QSharedPointer<QVector<T> > results(new QVector<T>(count));
for (int i=0; i<count; ++i) {
QPromise<T>(promises[i]).then([=](const T& res) mutable {
(*results)[i] = res;
if (--(*remaining) == 0) {
next.fulfill(*results);
resolve(*results);
}
}
}, [=]() mutable {
if (next.isPending()) {
next.reject(std::current_exception());
}
});
}
return next;
}, [=]() mutable {
if (*remaining != -1) {
*remaining = -1;
reject(std::current_exception());
}
});
}
});
}
template <typename T>
inline void QPromise<T>::notify(const typename QPromiseData<T>::HandlerList& handlers) const
inline QPromise<T> QPromise<T>::resolve(const T& value)
{
return QPromise<T>([=](const QPromiseResolve<T>& resolve) {
resolve(value);
});
}
template <typename T>
inline void QPromise<T>::notify(const typename QtPromisePrivate::PromiseData<T>::HandlerList& handlers) const
{
const T value = this->m_d->value;
qtpromise_defer([handlers, value]() {
QtPromisePrivate::qtpromise_defer([handlers, value]() {
for (auto handler: handlers) {
handler(value);
}
@ -154,52 +258,53 @@ template <typename THandler>
inline QPromise<void> QPromise<void>::finally(THandler handler)
{
return this->then([=]() {
return QPromise<void>().fulfill().then(handler).then([](){});
return QPromise<void>::resolve().then(handler).then([](){});
}, [=]() {
const auto exception = std::current_exception();
return QPromise<void>().fulfill().then(handler).then([=](){
return QPromise<void>::resolve().then(handler).then([=](){
std::rethrow_exception(exception);
});
});
}
inline QPromise<void> QPromise<void>::fulfill()
{
if (!m_d->resolved) {
m_d->rejected = false;
m_d->resolved = true;
dispatch();
}
return *this;
}
inline QPromise<void> QPromise<void>::all(const QVector<QPromise<void> >& promises)
{
QPromise<void> next;
QSharedPointer<int> remaining(new int(promises.size()));
for (const auto& promise: promises) {
QPromise<void>(promise).then([=]() mutable {
if (next.isPending()) {
if (--(*remaining) == 0) {
next.fulfill();
}
}
}, [=]() mutable {
if (next.isPending()) {
next.reject(std::current_exception());
}
});
const int count = promises.size();
if (count == 0) {
return QPromise<void>::resolve();
}
return next;
return QPromise<void>([=](
const QPromiseResolve<void>& resolve,
const QPromiseReject<void>& reject) {
QSharedPointer<int> remaining(new int(promises.size()));
for (const auto& promise: promises) {
QPromise<void>(promise).then([=]() {
if (--(*remaining) == 0) {
resolve();
}
}, [=]() {
if (*remaining != -1) {
*remaining = -1;
reject(std::current_exception());
}
});
}
});
}
inline void QPromise<void>::notify(const typename QPromiseData<void>::HandlerList& handlers) const
inline QPromise<void> QPromise<void>::resolve()
{
qtpromise_defer([handlers]() {
return QPromise<void>([](const QPromiseResolve<void>& resolve) {
resolve();
});
}
inline void QPromise<void>::notify(const typename QtPromisePrivate::PromiseData<void>::HandlerList& handlers) const
{
QtPromisePrivate::qtpromise_defer([handlers]() {
for (const auto& handler: handlers) {
handler();
}
@ -209,9 +314,23 @@ inline void QPromise<void>::notify(const typename QPromiseData<void>::HandlerLis
// Helpers
template <typename T>
QPromise<T> qPromise(const T& value)
typename QtPromisePrivate::PromiseDeduce<T>::Type qPromise(const T& value)
{
return QPromise<T>().fulfill(value);
using namespace QtPromisePrivate;
using Promise = typename PromiseDeduce<T>::Type;
return Promise([=](
const QPromiseResolve<typename Promise::Type>& resolve,
const QPromiseReject<typename Promise::Type>& reject) {
PromiseFulfill<T>::call(value, resolve, reject);
});
}
QPromise<void> qPromise()
{
return QPromise<void>([](
const QPromiseResolve<void>& resolve) {
resolve();
});
}
template <typename T>

View File

@ -1,7 +1,8 @@
#ifndef _QTPROMISE_QPROMISE_P_H
#define _QTPROMISE_QPROMISE_P_H
// QPromise
// QtPromise
#include "qpromiseerror.h"
#include "qpromiseglobal.h"
// Qt
@ -10,18 +11,21 @@
#include <QSharedData>
#include <QVector>
// STL
#include <functional>
namespace QtPromise {
template <typename T = void>
template <typename T>
class QPromise;
}
template <typename T>
class QPromiseResolve;
template <typename T>
class QPromiseReject;
} // namespace QtPromise
namespace QtPromisePrivate {
using namespace QtPromise;
template <typename F>
inline void qtpromise_defer(F f)
{
@ -29,265 +33,269 @@ inline void qtpromise_defer(F f)
}
template <typename T>
struct QPromiseDeduce
struct PromiseDeduce
{
using Type = QPromise<Unqualified<T> >;
using Type = QtPromise::QPromise<Unqualified<T> >;
};
template <typename T>
struct QPromiseDeduce<QPromise<T> >
: public QPromiseDeduce<T>
struct PromiseDeduce<QtPromise::QPromise<T> >
: public PromiseDeduce<T>
{ };
template <typename T, typename TPromise = typename QPromiseDeduce<T>::Type>
struct QPromiseFulfill
template <typename T>
struct PromiseFulfill
{
static void call(TPromise next, const T& value)
static void call(
const T& value,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>&)
{
next.fulfill(value);
resolve(value);
}
};
template <typename T>
struct QPromiseFulfill<QPromise<T>, QPromise<T> >
struct PromiseFulfill<QtPromise::QPromise<T> >
{
static void call(QPromise<T> next, QPromise<T> promise)
static void call(
QtPromise::QPromise<T> promise,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>& reject)
{
promise.then(
[=](const T& value) mutable {
next.fulfill(value);
[=](const T& value) {
resolve(value);
},
[=]() mutable {
next.reject(std::current_exception());
[=]() { // catch all
reject(std::current_exception());
});
}
};
template <>
struct QPromiseFulfill<QPromise<void>, QPromise<void> >
struct PromiseFulfill<QtPromise::QPromise<void> >
{
template <typename TPromise>
static void call(TPromise next, TPromise promise)
template <typename TPromise, typename TResolve, typename TReject>
static void call(TPromise promise, TResolve resolve, TReject reject)
{
promise.then(
[=]() mutable {
next.fulfill();
[=]() {
resolve();
},
[=]() mutable {
next.reject(std::current_exception());
[=]() { // catch all
reject(std::current_exception());
});
}
};
template <typename T, typename TRes>
struct QPromiseDispatch
struct PromiseDispatch
{
using Promise = typename QPromiseDeduce<TRes>::Type;
using Promise = typename PromiseDeduce<TRes>::Type;
using ResType = Unqualified<TRes>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler, const T& value)
template <typename THandler, typename TResolve, typename TReject>
static void call(const T& value, THandler handler, TResolve resolve, TReject reject)
{
ResType res;
try {
res = handler(value);
const ResType res = handler(value);
PromiseFulfill<ResType>::call(res, resolve, reject);
} catch (...) {
next.reject(std::current_exception());
return;
reject(std::current_exception());
}
QPromiseFulfill<ResType>::call(next, res);
}
};
template <typename T>
struct QPromiseDispatch<T, void>
struct PromiseDispatch<T, void>
{
using Promise = QPromise<void>;
using Promise = QtPromise::QPromise<void>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler, const T& value)
template <typename THandler, typename TResolve, typename TReject>
static void call(const T& value, THandler handler, TResolve resolve, TReject reject)
{
try {
handler(value);
resolve();
} catch (...) {
next.reject(std::current_exception());
return;
reject(std::current_exception());
}
next.fulfill();
}
};
template <typename TRes>
struct QPromiseDispatch<void, TRes>
struct PromiseDispatch<void, TRes>
{
using Promise = typename QPromiseDeduce<TRes>::Type;
using Promise = typename PromiseDeduce<TRes>::Type;
using ResType = Unqualified<TRes>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler)
template <typename THandler, typename TResolve, typename TReject>
static void call(THandler handler, TResolve resolve, TReject reject)
{
ResType res;
try {
res = handler();
const ResType res = handler();
PromiseFulfill<ResType>::call(res, resolve, reject);
} catch (...) {
next.reject(std::current_exception());
return;
reject(std::current_exception());
}
QPromiseFulfill<ResType>::call(next, res);
}
};
template <>
struct QPromiseDispatch<void, void>
struct PromiseDispatch<void, void>
{
using Promise = QPromise<void>;
using Promise = QtPromise::QPromise<void>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler)
template <typename THandler, typename TResolve, typename TReject>
static void call(THandler handler, TResolve resolve, TReject reject)
{
try {
handler();
resolve();
} catch (...) {
next.reject(std::current_exception());
return;
reject(std::current_exception());
}
next.fulfill();
}
};
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
struct QPromiseHandler
struct PromiseHandler
{
using ResType = typename std::result_of<THandler(T)>::type;
using Promise = typename QPromiseDispatch<T, ResType>::Promise;
using Promise = typename PromiseDispatch<T, ResType>::Promise;
static std::function<void(T)> create(const Promise& next, THandler handler)
template <typename TResolve, typename TReject>
static std::function<void(T)> create(THandler handler, TResolve resolve, TReject reject)
{
return [=](const T& value) mutable {
QPromiseDispatch<T, ResType>::call(next, handler, value);
return [=](const T& value) {
PromiseDispatch<T, ResType>::call(value, handler, resolve, reject);
};
}
};
template <typename T, typename THandler>
struct QPromiseHandler<T, THandler, void>
struct PromiseHandler<T, THandler, void>
{
using ResType = typename std::result_of<THandler()>::type;
using Promise = typename QPromiseDispatch<T, ResType>::Promise;
using Promise = typename PromiseDispatch<T, ResType>::Promise;
static std::function<void(T)> create(const Promise& next, THandler handler)
template <typename TResolve, typename TReject>
static std::function<void(T)> create(THandler handler, TResolve resolve, TReject reject)
{
return [=](const T&) mutable {
QPromiseDispatch<void, ResType>::call(next, handler);
return [=](const T&) {
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
};
}
};
template <typename THandler>
struct QPromiseHandler<void, THandler, void>
struct PromiseHandler<void, THandler, void>
{
using ResType = typename std::result_of<THandler()>::type;
using Promise = typename QPromiseDispatch<void, ResType>::Promise;
using Promise = typename PromiseDispatch<void, ResType>::Promise;
static std::function<void()> create(const Promise& next, THandler handler)
template <typename TResolve, typename TReject>
static std::function<void()> create(THandler handler, TResolve resolve, TReject reject)
{
return [=]() {
QPromiseDispatch<void, ResType>::call(next, handler);
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
};
}
};
template <typename T>
struct QPromiseHandler<T, std::nullptr_t, void>
struct PromiseHandler<T, std::nullptr_t, void>
{
using Promise = QPromise<T>;
using Promise = QtPromise::QPromise<T>;
static std::function<void(T)> create(const Promise& next, std::nullptr_t)
template <typename TResolve, typename TReject>
static std::function<void(T)> create(std::nullptr_t, TResolve resolve, TReject reject)
{
return [next](const T& value) mutable {
return [=](const T& value) {
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
// promise2 must be fulfilled with the same value as promise1.
QPromiseFulfill<T>::call(next, value);
PromiseFulfill<T>::call(value, resolve, reject);
};
}
};
template <>
struct QPromiseHandler<void, std::nullptr_t, void>
struct PromiseHandler<void, std::nullptr_t, void>
{
using Promise = QPromise<void>;
using Promise = QtPromise::QPromise<void>;
template <typename TPromise>
static std::function<void()> create(const TPromise& next, std::nullptr_t)
template <typename TResolve, typename TReject>
static std::function<void()> create(std::nullptr_t, TResolve resolve, TReject)
{
return [=]() mutable {
return [=]() {
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
// promise2 must be fulfilled with the same value as promise1.
TPromise(next).fulfill();
resolve();
};
}
};
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
struct QPromiseCatcher
struct PromiseCatcher
{
using Type = std::function<void(std::exception_ptr)>;
using Functor = std::function<void(QtPromise::QPromiseError)>;
using ResType = typename std::result_of<THandler(TArg)>::type;
template <typename TPromise>
static Type create(const TPromise& next, THandler handler)
template <typename TResolve, typename TReject>
static Functor create(THandler handler, TResolve resolve, TReject reject)
{
return [=](const std::exception_ptr& eptr) mutable {
return [=](const QtPromise::QPromiseError& error) {
try {
std::rethrow_exception(eptr);
error.rethrow();
} catch (const TArg& error) {
QPromiseDispatch<TArg, ResType>::call(next, handler, error);
PromiseDispatch<TArg, ResType>::call(error, handler, resolve, reject);
} catch(...) {
TPromise(next).reject(std::current_exception());
reject(std::current_exception());
}
};
}
};
template <typename T, typename THandler>
struct QPromiseCatcher<T, THandler, void>
struct PromiseCatcher<T, THandler, void>
{
using Type = std::function<void(std::exception_ptr)>;
using Functor = std::function<void(QtPromise::QPromiseError)>;
using ResType = typename std::result_of<THandler()>::type;
template <typename TPromise>
static Type create(const TPromise& next, THandler handler)
template <typename TResolve, typename TReject>
static Functor create(THandler handler, TResolve resolve, TReject reject)
{
return [=](const std::exception_ptr& eptr) mutable {
return [=](const QtPromise::QPromiseError& error) {
try {
std::rethrow_exception(eptr);
error.rethrow();
} catch (...) {
QPromiseDispatch<void, ResType>::call(next, handler);
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
}
};
}
};
template <typename T>
struct QPromiseCatcher<T, std::nullptr_t, void>
struct PromiseCatcher<T, std::nullptr_t, void>
{
using Type = std::function<void(std::exception_ptr)>;
using Functor = std::function<void(QtPromise::QPromiseError)>;
template <typename TPromise>
static Type create(const TPromise& next, std::nullptr_t)
template <typename TResolve, typename TReject>
static Functor create(std::nullptr_t, TResolve, TReject reject)
{
return [=](const std::exception_ptr& eptr) mutable {
return [=](const QtPromise::QPromiseError& error) {
// 2.2.7.4. If onRejected is not a function and promise1 is rejected,
// promise2 must be rejected with the same reason as promise1
TPromise(next).reject(eptr);
reject(error);
};
}
};
template <class T>
struct QPromiseDataBase: public QSharedData
struct PromiseDataBase: public QSharedData
{
using ErrorType = std::exception_ptr;
using ErrorType = QtPromise::QPromiseError;
using CatcherList = QVector<std::function<void(ErrorType)> >;
bool resolved;
@ -297,7 +305,7 @@ struct QPromiseDataBase: public QSharedData
};
template <typename T>
struct QPromiseData: QPromiseDataBase<T>
struct PromiseData: PromiseDataBase<T>
{
using HandlerList = QVector<std::function<void(T)> >;
@ -306,7 +314,7 @@ struct QPromiseData: QPromiseDataBase<T>
};
template <>
struct QPromiseData<void>: QPromiseDataBase<void>
struct PromiseData<void>: PromiseDataBase<void>
{
using HandlerList = QVector<std::function<void()> >;

View File

@ -4,76 +4,61 @@
namespace QtPromisePrivate {
template <typename T>
struct QPromiseDeduce<QFuture<T> >
: public QPromiseDeduce<T>
struct PromiseDeduce<QFuture<T> >
: public PromiseDeduce<T>
{ };
template <typename T>
struct QPromiseFulfill<QFuture<T>, QPromise<T> >
struct PromiseFulfill<QFuture<T> >
{
static void call(QPromise<T> next, const QFuture<T>& future)
static void call(
const QFuture<T>& future,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>& reject)
{
using Watcher = QFutureWatcher<T>;
Watcher* watcher = new Watcher();
QObject::connect(
watcher, &Watcher::finished,
[next, watcher]() mutable {
T res;
try {
res = watcher->result();
} catch(...) {
next.reject(std::current_exception());
}
QObject::connect(watcher, &Watcher::finished, [=]() mutable {
try {
T res = watcher->result();
PromiseFulfill<T>::call(res, resolve, reject);
} catch(...) {
reject(std::current_exception());
}
watcher->deleteLater();
if (next.isPending()) {
QPromiseFulfill<T>::call(next, res);
}
});
watcher->deleteLater();
});
watcher->setFuture(future);
}
};
template <>
struct QPromiseFulfill<QFuture<void>, QPromise<void> >
struct PromiseFulfill<QFuture<void> >
{
static void call(QPromise<void> next, const QFuture<void>& future)
static void call(
const QFuture<void>& future,
const QtPromise::QPromiseResolve<void>& resolve,
const QtPromise::QPromiseReject<void>& reject)
{
using Watcher = QFutureWatcher<void>;
Watcher* watcher = new Watcher();
QObject::connect(
watcher, &Watcher::finished,
[next, watcher]() mutable {
try {
// let's rethrown possibe exception
watcher->waitForFinished();
} catch(...) {
next.reject(std::current_exception());
}
QObject::connect(watcher, &Watcher::finished, [=]() mutable {
try {
// let's rethrown possibe exception
watcher->waitForFinished();
resolve();
} catch(...) {
reject(std::current_exception());
}
watcher->deleteLater();
if (next.isPending()) {
next.fulfill();
}
});
watcher->deleteLater();
});
watcher->setFuture(future);
}
};
} // namespace QtPromisePrivate
namespace QtPromise {
template <typename T>
QPromise<T> qPromise(const QFuture<T>& future)
{
QPromise<T> next;
QPromiseFulfill<QFuture<T> >::call(next, future);
return next;
}
} // namespace QtPromise

View File

@ -0,0 +1,42 @@
#ifndef _QTPROMISE_QPROMISEERROR_H
#define _QTPROMISE_QPROMISEERROR_H
// QtPromise
#include "qpromiseglobal.h"
namespace QtPromise {
class QPromiseError
{
public:
QPromiseError()
: exception(nullptr)
{ }
template <typename T>
QPromiseError(const T& value)
: exception(nullptr)
{
try {
throw value;
} catch(...) {
exception = std::current_exception();
}
}
QPromiseError(const std::exception_ptr& exception)
: exception(exception)
{ }
void rethrow() const
{
std::rethrow_exception(exception);
}
private:
std::exception_ptr exception;
};
} // namespace QtPromise
#endif // _QTPROMISE_QPROMISEERROR_H

View File

@ -6,6 +6,7 @@
// STL
#include <functional>
#include <array>
namespace QtPromisePrivate
{
@ -13,6 +14,22 @@ namespace QtPromisePrivate
template <typename T>
using Unqualified = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
/*!
* \struct HasCallOperator
* http://stackoverflow.com/a/5839442
*/
template <typename T>
struct HasCallOperator
{
template <class U>
static auto check(const U* u)
-> decltype(&U::operator(), char(0));
static std::array<char, 2> check(...);
static const bool value = (sizeof(check((T*)0)) == 1);
};
/*!
* \struct ArgsOf
* http://stackoverflow.com/a/7943765
@ -23,6 +40,7 @@ struct ArgsTraits
{
using types = std::tuple<Args...>;
using first = typename std::tuple_element<0, types>::type;
static const size_t count = std::tuple_size<types>::value;
};
template <>
@ -30,14 +48,16 @@ struct ArgsTraits<>
{
using types = std::tuple<>;
using first = void;
static const size_t count = 0;
};
template <typename T>
struct ArgsOf : public ArgsOf<decltype(&T::operator())>
template <typename T, typename Enable = void>
struct ArgsOf : public ArgsTraits<>
{ };
template <>
struct ArgsOf<std::nullptr_t> : public ArgsTraits<>
template <typename T>
struct ArgsOf<T, typename std::enable_if<HasCallOperator<T>::value>::type>
: public ArgsOf<decltype(&T::operator())>
{ };
template <typename TReturn, typename... Args>
@ -100,25 +120,6 @@ template <typename T>
struct ArgsOf<const volatile T&&> : public ArgsOf<T>
{ };
/*!
* \fn to_exception_ptr
*/
template <typename T>
std::exception_ptr to_exception_ptr(const T& value)
{
try {
throw value;
} catch(...) {
return std::current_exception();
}
}
template <>
std::exception_ptr to_exception_ptr(const std::exception_ptr& value)
{
return value;
}
} // namespace QtPromisePrivate
#endif // ifndef _QTPROMISE_QPROMISEGLOBAL_H

View File

@ -3,4 +3,5 @@ HEADERS += \
$$PWD/qpromise.inl \
$$PWD/qpromise_p.h \
$$PWD/qpromise_qfuture.inl \
$$PWD/qpromiseerror.h \
$$PWD/qpromiseglobal.h