Initial implementation

This commit is contained in:
Simon Brunel
2017-05-14 19:03:01 +02:00
commit 6a642446df
21 changed files with 1433 additions and 0 deletions

107
src/qtpromise/qpromise.h Normal file
View File

@ -0,0 +1,107 @@
#ifndef _QTPROMISE_QPROMISE_H
#define _QTPROMISE_QPROMISE_H
// QPromise
#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; }
bool isRejected() const { return m_d->resolved && m_d->rejected; }
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;
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 QPromise<T> wait() const;
protected:
QExplicitlySharedDataPointer<QPromiseData<T> > m_d;
virtual void notify(const typename QPromiseData<T>::HandlerList& handlers) const = 0;
inline void dispatch();
};
template <typename T>
class QPromise: public QPromiseBase<T>
{
public:
QPromise() : QPromiseBase<T>() {}
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);
protected:
inline void notify(const typename QPromiseData<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 THandler>
inline QPromise<void> finally(THandler handler);
inline QPromise<void> fulfill();
public: // STATIC
inline static QPromise<void> all(const QVector<QPromise<void> >& promises);
protected:
inline void notify(const typename QPromiseData<void>::HandlerList& handlers) const Q_DECL_OVERRIDE;
private:
friend class QPromiseBase<void>;
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"
#endif // ifndef _QTPROMISE_QPROMISE_H

228
src/qtpromise/qpromise.inl Normal file
View File

@ -0,0 +1,228 @@
// Qt
#include <QCoreApplication>
#include <QSharedPointer>
namespace QtPromise {
template <typename T>
template <typename TFulfilled, typename TRejected>
inline auto QPromiseBase<T>::then(TFulfilled fulfilled, TRejected rejected)
-> typename QPromiseHandler<T, TFulfilled>::Promise
{
typename QPromiseHandler<T, TFulfilled>::Promise next;
m_d->handlers << QPromiseHandler<T, TFulfilled>::create(next, fulfilled);
m_d->catchers << QPromiseCatcher<T, TRejected>::create(next, rejected);
if (m_d->resolved) {
dispatch();
}
return next;
}
template <typename T>
template <typename TRejected>
inline auto QPromiseBase<T>::fail(TRejected rejected)
-> typename QPromiseHandler<T, std::nullptr_t>::Promise
{
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
{
// @TODO wait timeout + global timeout
while (!m_d->resolved) {
QCoreApplication::processEvents(QEventLoop::AllEvents);
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
}
return *this;
}
template <typename T>
inline void QPromiseBase<T>::dispatch()
{
Q_ASSERT(m_d->resolved);
typename QPromiseData<T>::HandlerList handlers;
typename QPromiseData<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]() {
for (auto catcher: catchers) {
catcher(error);
}
});
} else {
notify(handlers);
}
}
template <typename T>
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 res;
});
}, [=]() {
const auto exception = std::current_exception();
return QPromise<void>().fulfill().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({});
}
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 {
if (next.isPending()) {
(*results)[i] = res;
if (--(*remaining) == 0) {
next.fulfill(*results);
}
}
}, [=]() mutable {
if (next.isPending()) {
next.reject(std::current_exception());
}
});
}
return next;
}
template <typename T>
inline void QPromise<T>::notify(const typename QPromiseData<T>::HandlerList& handlers) const
{
const T value = this->m_d->value;
qtpromise_defer([handlers, value]() {
for (auto handler: handlers) {
handler(value);
}
});
}
template <typename THandler>
inline QPromise<void> QPromise<void>::finally(THandler handler)
{
return this->then([=]() {
return QPromise<void>().fulfill().then(handler).then([](){});
}, [=]() {
const auto exception = std::current_exception();
return QPromise<void>().fulfill().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());
}
});
}
return next;
}
inline void QPromise<void>::notify(const typename QPromiseData<void>::HandlerList& handlers) const
{
qtpromise_defer([handlers]() {
for (const auto& handler: handlers) {
handler();
}
});
}
// Helpers
template <typename T>
QPromise<T> qPromise(const T& value)
{
return QPromise<T>().fulfill(value);
}
template <typename T>
QPromise<QVector<T> > qPromiseAll(const QVector<QPromise<T> >& promises)
{
return QPromise<T>::all(promises);
}
QPromise<void> qPromiseAll(const QVector<QPromise<void> >& promises)
{
return QPromise<void>::all(promises);
}
} // namespace QtPromise

318
src/qtpromise/qpromise_p.h Normal file
View File

@ -0,0 +1,318 @@
#ifndef _QTPROMISE_QPROMISE_P_H
#define _QTPROMISE_QPROMISE_P_H
// QPromise
#include "qpromiseglobal.h"
// Qt
#include <QCoreApplication>
#include <QTimer>
#include <QSharedData>
#include <QVector>
// STL
#include <functional>
namespace QtPromise {
template <typename T = void>
class QPromise;
}
namespace QtPromisePrivate {
using namespace QtPromise;
template <typename F>
inline void qtpromise_defer(F f)
{
QTimer::singleShot(0, f);
}
template <typename T>
struct QPromiseDeduce
{
using Type = QPromise<Unqualified<T> >;
};
template <typename T>
struct QPromiseDeduce<QPromise<T> >
: public QPromiseDeduce<T>
{ };
template <typename T, typename TPromise = typename QPromiseDeduce<T>::Type>
struct QPromiseFulfill
{
static void call(TPromise next, const T& value)
{
next.fulfill(value);
}
};
template <typename T>
struct QPromiseFulfill<QPromise<T>, QPromise<T> >
{
static void call(QPromise<T> next, QPromise<T> promise)
{
promise.then(
[=](const T& value) mutable {
next.fulfill(value);
},
[=]() mutable {
next.reject(std::current_exception());
});
}
};
template <>
struct QPromiseFulfill<QPromise<void>, QPromise<void> >
{
template <typename TPromise>
static void call(TPromise next, TPromise promise)
{
promise.then(
[=]() mutable {
next.fulfill();
},
[=]() mutable {
next.reject(std::current_exception());
});
}
};
template <typename T, typename TRes>
struct QPromiseDispatch
{
using Promise = typename QPromiseDeduce<TRes>::Type;
using ResType = Unqualified<TRes>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler, const T& value)
{
ResType res;
try {
res = handler(value);
} catch (...) {
next.reject(std::current_exception());
return;
}
QPromiseFulfill<ResType>::call(next, res);
}
};
template <typename T>
struct QPromiseDispatch<T, void>
{
using Promise = QPromise<void>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler, const T& value)
{
try {
handler(value);
} catch (...) {
next.reject(std::current_exception());
return;
}
next.fulfill();
}
};
template <typename TRes>
struct QPromiseDispatch<void, TRes>
{
using Promise = typename QPromiseDeduce<TRes>::Type;
using ResType = Unqualified<TRes>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler)
{
ResType res;
try {
res = handler();
} catch (...) {
next.reject(std::current_exception());
return;
}
QPromiseFulfill<ResType>::call(next, res);
}
};
template <>
struct QPromiseDispatch<void, void>
{
using Promise = QPromise<void>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler)
{
try {
handler();
} catch (...) {
next.reject(std::current_exception());
return;
}
next.fulfill();
}
};
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
struct QPromiseHandler
{
using ResType = typename std::result_of<THandler(T)>::type;
using Promise = typename QPromiseDispatch<T, ResType>::Promise;
static std::function<void(T)> create(const Promise& next, THandler handler)
{
return [=](const T& value) mutable {
QPromiseDispatch<T, ResType>::call(next, handler, value);
};
}
};
template <typename T, typename THandler>
struct QPromiseHandler<T, THandler, void>
{
using ResType = typename std::result_of<THandler()>::type;
using Promise = typename QPromiseDispatch<T, ResType>::Promise;
static std::function<void(T)> create(const Promise& next, THandler handler)
{
return [=](const T&) mutable {
QPromiseDispatch<void, ResType>::call(next, handler);
};
}
};
template <typename THandler>
struct QPromiseHandler<void, THandler, void>
{
using ResType = typename std::result_of<THandler()>::type;
using Promise = typename QPromiseDispatch<void, ResType>::Promise;
static std::function<void()> create(const Promise& next, THandler handler)
{
return [=]() {
QPromiseDispatch<void, ResType>::call(next, handler);
};
}
};
template <typename T>
struct QPromiseHandler<T, std::nullptr_t, void>
{
using Promise = QPromise<T>;
static std::function<void(T)> create(const Promise& next, std::nullptr_t)
{
return [next](const T& value) mutable {
// 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);
};
}
};
template <>
struct QPromiseHandler<void, std::nullptr_t, void>
{
using Promise = QPromise<void>;
template <typename TPromise>
static std::function<void()> create(const TPromise& next, std::nullptr_t)
{
return [=]() mutable {
// 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();
};
}
};
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
struct QPromiseCatcher
{
using Type = std::function<void(std::exception_ptr)>;
using ResType = typename std::result_of<THandler(TArg)>::type;
template <typename TPromise>
static Type create(const TPromise& next, THandler handler)
{
return [=](const std::exception_ptr& eptr) mutable {
try {
std::rethrow_exception(eptr);
} catch (const TArg& error) {
QPromiseDispatch<TArg, ResType>::call(next, handler, error);
} catch(...) {
TPromise(next).reject(std::current_exception());
}
};
}
};
template <typename T, typename THandler>
struct QPromiseCatcher<T, THandler, void>
{
using Type = std::function<void(std::exception_ptr)>;
using ResType = typename std::result_of<THandler()>::type;
template <typename TPromise>
static Type create(const TPromise& next, THandler handler)
{
return [=](const std::exception_ptr& eptr) mutable {
try {
std::rethrow_exception(eptr);
} catch (...) {
QPromiseDispatch<void, ResType>::call(next, handler);
}
};
}
};
template <typename T>
struct QPromiseCatcher<T, std::nullptr_t, void>
{
using Type = std::function<void(std::exception_ptr)>;
template <typename TPromise>
static Type create(const TPromise& next, std::nullptr_t)
{
return [=](const std::exception_ptr& eptr) mutable {
// 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);
};
}
};
template <class T>
struct QPromiseDataBase: public QSharedData
{
using ErrorType = std::exception_ptr;
using CatcherList = QVector<std::function<void(ErrorType)> >;
bool resolved;
bool rejected;
ErrorType error;
CatcherList catchers;
};
template <typename T>
struct QPromiseData: QPromiseDataBase<T>
{
using HandlerList = QVector<std::function<void(T)> >;
HandlerList handlers;
T value;
};
template <>
struct QPromiseData<void>: QPromiseDataBase<void>
{
using HandlerList = QVector<std::function<void()> >;
HandlerList handlers;
};
} // namespace QtPromise
#endif // ifndef _QTPROMISE_QPROMISE_H

View File

@ -0,0 +1,79 @@
#include <QFutureWatcher>
#include <QFuture>
namespace QtPromisePrivate {
template <typename T>
struct QPromiseDeduce<QFuture<T> >
: public QPromiseDeduce<T>
{ };
template <typename T>
struct QPromiseFulfill<QFuture<T>, QPromise<T> >
{
static void call(QPromise<T> next, const QFuture<T>& future)
{
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());
}
watcher->deleteLater();
if (next.isPending()) {
QPromiseFulfill<T>::call(next, res);
}
});
watcher->setFuture(future);
}
};
template <>
struct QPromiseFulfill<QFuture<void>, QPromise<void> >
{
static void call(QPromise<void> next, const QFuture<void>& future)
{
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());
}
watcher->deleteLater();
if (next.isPending()) {
next.fulfill();
}
});
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,124 @@
#ifndef _QTPROMISE_QPROMISEGLOBAL_H
#define _QTPROMISE_QPROMISEGLOBAL_H
// QtCore
#include <QtGlobal>
// STL
#include <functional>
namespace QtPromisePrivate
{
// https://rmf.io/cxx11/even-more-traits#unqualified_types
template <typename T>
using Unqualified = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
/*!
* \struct ArgsOf
* http://stackoverflow.com/a/7943765
* http://stackoverflow.com/a/27885283
*/
template <typename... Args>
struct ArgsTraits
{
using types = std::tuple<Args...>;
using first = typename std::tuple_element<0, types>::type;
};
template <>
struct ArgsTraits<>
{
using types = std::tuple<>;
using first = void;
};
template <typename T>
struct ArgsOf : public ArgsOf<decltype(&T::operator())>
{ };
template <>
struct ArgsOf<std::nullptr_t> : public ArgsTraits<>
{ };
template <typename TReturn, typename... Args>
struct ArgsOf<TReturn(Args...)> : public ArgsTraits<Args...>
{ };
template <typename TReturn, typename... Args>
struct ArgsOf<TReturn(*)(Args...)> : public ArgsTraits<Args...>
{ };
template <typename T, typename TReturn, typename... Args>
struct ArgsOf<TReturn(T::*)(Args...)> : public ArgsTraits<Args...>
{ };
template <typename T, typename TReturn, typename... Args>
struct ArgsOf<TReturn(T::*)(Args...) const> : public ArgsTraits<Args...>
{ };
template <typename T, typename TReturn, typename... Args>
struct ArgsOf<TReturn(T::*)(Args...) volatile> : public ArgsTraits<Args...>
{ };
template <typename T, typename TReturn, typename... Args>
struct ArgsOf<TReturn(T::*)(Args...) const volatile> : public ArgsTraits<Args...>
{ };
template <typename T>
struct ArgsOf<std::function<T> > : public ArgsOf<T>
{ };
template <typename T>
struct ArgsOf<T&> : public ArgsOf<T>
{ };
template <typename T>
struct ArgsOf<const T&> : public ArgsOf<T>
{ };
template <typename T>
struct ArgsOf<volatile T&> : public ArgsOf<T>
{ };
template <typename T>
struct ArgsOf<const volatile T&> : public ArgsOf<T>
{ };
template <typename T>
struct ArgsOf<T&&> : public ArgsOf<T>
{ };
template <typename T>
struct ArgsOf<const T&&> : public ArgsOf<T>
{ };
template <typename T>
struct ArgsOf<volatile T&&> : public ArgsOf<T>
{ };
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

@ -0,0 +1,6 @@
HEADERS += \
$$PWD/qpromise.h \
$$PWD/qpromise.inl \
$$PWD/qpromise_p.h \
$$PWD/qpromise_qfuture.inl \
$$PWD/qpromiseglobal.h

View File

@ -0,0 +1,5 @@
TEMPLATE = lib
CONFIG += c++11 qt thread warn_on
DEFINES += QT_DEPRECATED_WARNINGS
include(qtpromise.pri)

2
src/src.pro Normal file
View File

@ -0,0 +1,2 @@
TEMPLATE = subdirs
SUBDIRS = qtpromise