Promises/A+ implementation for Qt/C++.
Requires Qt 5.6 (or later) with C++11 support enabled or Qt 6.
QtPromise is available under the MIT license.
Promises/A+ implementation for Qt/C++
Home Page: https://qtpromise.netlify.com
License: MIT License
Promises/A+ implementation for Qt/C++.
Requires Qt 5.6 (or later) with C++11 support enabled or Qt 6.
QtPromise is available under the MIT license.
Hey there,
would it be possible to add API or an example on how to wrap promises of different types? E.g. to handle a scenario such as:
Ideally something like this:
auto a = qPromise(QtConcurrent::run([](){return QStringLiteral("foo");}));
auto b = qPromise(QtConcurrent::run([](){return 42;}));
qPromiseAll(a, b).then([](const QString &str, int num) {});
I believe one could handle this manually by building a promise that stores a tuple of return args (here QString and int) as well as a counter to check how many of the child promises have been fulfilled. Then when a child promise is fulfilled store its return type and decrement the counter, and if that reaches zero resolve the parent promise? And if any child promise gets rejected, also reject the parent promise?
We can connect to a Qt signal by using
// [signal] Object::finished(const QString&)
auto output = QtPromise::connect(obj, &Object::finished)
.then([](const QString& data) {
// use the data here to do further work
});
How to implement a promise only resolved when not only the signal emitted but also meet a specific condition?
// task with int task_id
QPromise<int> task = {...}
// [signal] Object::finished(const int&)
task.then([](const int& task_id){
QObject* context = new QObject(this);
return QtPromise::QPromise<Qstring>{[=](const auto& resolve, const auto& reject) {
QObject::connect(this, &Object::finished, context, [=](const int& finshed_id) {
if (task_id == finished_id)
{
resolve("task{} is finished, we can proceed to next", task_id);
delete context;
}
});
})
Would this work? Any elegant solution to handle the context
deletion (for disconnect the lambda expression)?
Hi,
I'm playing around with qtpromise and have situation where I'm launching a function using QtConcurrent:run() from an non-qt thread which is owned by an 3rdparty API. Using qtpromise in this situation appears not to be working, as the watcher in PromiseFulfill can not do its job without an eventloop and qtpromise_defer() would be using QThread::currentThread() which in my situation is the API thread also without an event loop.
I started to implement support for that, by adding a parameter to QtPromise which takes a pointer to a QThread which is passed down to PromiseFulfill::call(), where I move the watcher to that thread. In addition I added a parameter to .then() to also take a thread which is passed to the handlers and catchers which allow qtpromise_defer() to have a valid thread to post the event to.
Is that kind of functionality of general interest?
If so, what do you think would be better to use as a context, a thread or an object?
(Small recap of #6 (comment)): When setting up promises from Qt signals, the signal connections used for resolving are tied to the lifetime of the signal source only, i.e. not to the state of the QPromise
they resolve/reject. This is fine for signal sources that are already designed as promises to be deleted after resolve (like QNetworkReply
).
However, if a signal source is a persistent object there is no intrinsic mechanism for tearing down signal connections after resolve/reject. This is an easy to exploit memory leak and may cause other problems if the lifetime of other captured resources is crucial to an applications logic (RAII).
Unfortunately, keeping track of signal connections and disconnecting them all at once is kind of an ugly task. I ended up creating ConnectionGuard
for conveniently collecting and disconnecting multiple signals in the context of QPromise
construction (pwuertz@09a709e):
// -- In promise-returning method --
auto connections = ConnectionGuard::create();
return QPromise<void>([&](const auto& resolve, const auto& reject) {
// Resolve on signal1
*connections << connect(this, &Class::signal1, this, [=]() {
resolve();
});
// Reject on signal2
*connections << connect(this, &Class::signal2, this, [=]() {
reject();
});
// Reject on signal3 if condition is met
*connections << connect(this, &Class::signal3, this, [=]() {
if (condition) reject();
});
}).finally([=]() {
connections->disconnect();
});
The next step would be a high level API for creating a QPromise
from resolve and reject signals that automatically connects to and disconnects from signal sources like your suggestion:
qPromise(QObject* fulfiller, F fsignal, QObject* rejecter, R rsignal)
I think this could cover a lot of use cases.
However, in some scenarios I still ended up with stale situations where none of the signals would emit. Possible problems are:
destroyed
signal as rejection.So it would seem to me that there is a high chance of requiring more than one reason for rejection, which is why I'd be keen on the idea of allowing multiple rejection signals.
I know you prefer the idea of handling such cases by race
, but that implies that there must be a cancellation
feature as well. This is of course a powerful tool but certainly more difficult to implement and from what I've read cancellation is still a highly discussed topic without general consensus (unlike the A+ pattern).
In visual studio 2013 the test projects do not compile.
What is the minimum version of compilers to use the software?
Here is my test code:
QObject *testObj = new QObject();
QObject::connect(testObj,&QObject::destroyed,[]{
qInfo() << "obj deleted.";
});
auto promsie = ...
promise.then([=]{
testObj->deleteLater();
});
Hey all,
I stumbled upon this library, and it's looking very good - many thanks for working on it! I'm so far playing around with the code only, and wanted to solve a hypothetical problem which mixes QtConcurrent and Signal/Slot handling. E.g. I want to solve something like:
A
in background threadB
using event loopSo A
could be a one of the QtConcurrent
examples from https://qtpromise.netlify.com/qtpromise/qtconcurrent.html#convert, and B
would be the download
example from https://qtpromise.netlify.com/qtpromise/getting-started.html#example
What I fail to see is: How would I chain the two things together? qPromise
obviously doesn't forward the resolver to the QtConcurrent task. I apparently also cannot return a QtPromise
from a QtConcurrent
task either, i.e. this doesn't compile for me:
#include <QCoreApplication>
#include <QTimer>
#include <QtConcurrent/QtConcurrent>
#include <QtPromise>
#include <QDebug>
using namespace QtPromise;
QPromise<QString> foo(const QString& foo, const QString& bar)
{
return QPromise<QString>([foo, bar](const QPromiseResolve<QString>& resolve,
const QPromiseReject<QString>& reject) {
// simulate async signal handling
QTimer::singleShot(500, [foo, bar, resolve]() {
resolve(foo + bar);
});
});
}
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
auto p = qPromise(QtConcurrent::run(foo, QStringLiteral("foo"), QStringLiteral("bar")))
.then([&app](const QString& msg) { qDebug() << "got msg:" << msg; app.quit(); });
return app.exec();
}
The errors I get are:
In file included from /home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise.inl:2,
from /home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise.h:131,
from /home/milian/projects/bugs/async/qtpromise/include/QtPromise:4,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:6:
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromisehelpers.h: In instantiation of 'typename QtPromisePrivate::PromiseDeduce<T>::Type QtPromise::qPromise(T&&) [with T = QFuture<QtPromise::QPromise<QString> >; typename QtPromisePrivate::PromiseDeduce<T>::Type = QtPromise::QPromise<QString>]':
/home/milian/projects/bugs/async/qpromise_kitchensink.cpp:77:89: required from here
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromisehelpers.h:17:32: error: no matching function for call to 'QtPromisePrivate::PromiseFulfill<QFuture<QtPromise::QPromise<QString> > >::call(QFuture<QtPromise::QPromise<QString> >, const QtPromise::QPromiseResolve<QString>&, const QtPromise::QPromiseReject<QString>&)'
PromiseFulfill<T>::call(std::forward<T>(value), resolve, reject);
~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /home/milian/projects/bugs/async/qtpromise/include/QtPromise:5,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:6:
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromisefuture.h:32:17: note: candidate: 'static void QtPromisePrivate::PromiseFulfill<QFuture<T> >::call(const QFuture<T>&, const QtPromise::QPromiseResolve<T>&, const QtPromise::QPromiseReject<T>&) [with T = QtPromise::QPromise<QString>]'
static void call(
^~~~
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromisefuture.h:32:17: note: no known conversion for argument 2 from 'const QtPromise::QPromiseResolve<QString>' to 'const QtPromise::QPromiseResolve<QtPromise::QPromise<QString> >&'
In file included from /usr/include/qt/QtConcurrent/qtconcurrentrun.h:49,
from /usr/include/qt/QtConcurrent/QtConcurrent:16,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:5:
/usr/include/qt/QtConcurrent/qtconcurrentstoredfunctioncall.h: In instantiation of 'QtConcurrent::StoredFunctorCall2<T, FunctionPointer, Arg1, Arg2>::StoredFunctorCall2(FunctionPointer, const Arg1&, const Arg2&) [with T = QtPromise::QPromise<QString>; FunctionPointer = QtPromise::QPromise<QString> (*)(const QString&, const QString&); Arg1 = QString; Arg2 = QString]':
/usr/include/qt/QtConcurrent/qtconcurrentrun.h:84:13: required from 'QFuture<T> QtConcurrent::run(T (*)(Param1, Param2), const Arg1&, const Arg2&) [with T = QtPromise::QPromise<QString>; Param1 = const QString&; Arg1 = QString; Param2 = const QString&; Arg2 = QString]'
/home/milian/projects/bugs/async/qpromise_kitchensink.cpp:77:88: required from here
/usr/include/qt/QtConcurrent/qtconcurrentstoredfunctioncall.h:783:53: error: use of deleted function 'QtConcurrent::RunFunctionTask<QtPromise::QPromise<QString> >::RunFunctionTask()'
: function(_function), arg1(_arg1), arg2(_arg2) {}
^
In file included from /usr/include/qt/QtConcurrent/qtconcurrentrun.h:48,
from /usr/include/qt/QtConcurrent/QtConcurrent:16,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:5:
/usr/include/qt/QtConcurrent/qtconcurrentrunbase.h:96:7: note: 'QtConcurrent::RunFunctionTask<QtPromise::QPromise<QString> >::RunFunctionTask()' is implicitly deleted because the default definition would be ill-formed:
class RunFunctionTask : public RunFunctionTaskBase<T>
^~~~~~~~~~~~~~~
/usr/include/qt/QtConcurrent/qtconcurrentrunbase.h:96:7: error: no matching function for call to 'QtPromise::QPromise<QString>::QPromise()'
In file included from /home/milian/projects/bugs/async/qtpromise/include/QtPromise:4,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:6:
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise.h:89:5: note: candidate: 'template<class F> QtPromise::QPromise<T>::QPromise(F&&)'
QPromise(F&& resolver): QPromiseBase<T>(std::forward<F>(resolver)) { }
^~~~~~~~
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise.h:89:5: note: template argument deduction/substitution failed:
In file included from /usr/include/qt/QtConcurrent/qtconcurrentrun.h:48,
from /usr/include/qt/QtConcurrent/QtConcurrent:16,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:5:
/usr/include/qt/QtConcurrent/qtconcurrentrunbase.h:96:7: note: candidate expects 1 argument, 0 provided
class RunFunctionTask : public RunFunctionTaskBase<T>
^~~~~~~~~~~~~~~
In file included from /home/milian/projects/bugs/async/qtpromise/include/QtPromise:4,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:6:
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise.h:85:7: note: candidate: 'QtPromise::QPromise<QString>::QPromise(const QtPromise::QPromise<QString>&)'
class QPromise : public QPromiseBase<T>
^~~~~~~~
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise.h:85:7: note: candidate expects 1 argument, 0 provided
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise.h:85:7: note: candidate: 'QtPromise::QPromise<QString>::QPromise(QtPromise::QPromise<QString>&&)'
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise.h:85:7: note: candidate expects 1 argument, 0 provided
In file included from /home/milian/projects/bugs/async/qtpromise/include/QtPromise:5,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:6:
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromisefuture.h: In instantiation of 'static void QtPromisePrivate::PromiseFulfill<QFuture<T> >::call(const QFuture<T>&, const QtPromise::QPromiseResolve<T>&, const QtPromise::QPromiseReject<T>&) [with T = QtPromise::QPromise<QString>]':
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromisehelpers.h:17:32: required from 'typename QtPromisePrivate::PromiseDeduce<T>::Type QtPromise::qPromise(T&&) [with T = QFuture<QtPromise::QPromise<QString> >; typename QtPromisePrivate::PromiseDeduce<T>::Type = QtPromise::QPromise<QString>]'
/home/milian/projects/bugs/async/qpromise_kitchensink.cpp:77:89: required from here
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromisefuture.h:51:44: error: no matching function for call to 'QtPromisePrivate::PromiseFulfill<QtPromise::QPromise<QString> >::call(QtPromise::QPromise<QString>, const QtPromise::QPromiseResolve<QtPromise::QPromise<QString> >&, const QtPromise::QPromiseReject<QtPromise::QPromise<QString> >&)'
PromiseFulfill<T>::call(watcher->result(), resolve, reject);
~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise.h:5,
from /home/milian/projects/bugs/async/qtpromise/include/QtPromise:4,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:6:
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise_p.h:144:17: note: candidate: 'static void QtPromisePrivate::PromiseFulfill<QtPromise::QPromise<T> >::call(const QtPromise::QPromise<T>&, const QtPromise::QPromiseResolve<T>&, const QtPromise::QPromiseReject<T>&) [with T = QString]'
static void call(
^~~~
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise_p.h:144:17: note: no known conversion for argument 2 from 'const QtPromise::QPromiseResolve<QtPromise::QPromise<QString> >' to 'const QtPromise::QPromiseResolve<QString>&'
In file included from /home/milian/projects/bugs/async/qtpromise/include/QtPromise:4,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:6:
/home/milian/projects/bugs/async/qtpromise/include/../src/qtpromise/qpromise.h:89:5: error: 'QtPromise::QPromise<T>::QPromise(F&&) [with F = QtPromise::qPromise(T&&) [with T = QFuture<QtPromise::QPromise<QString> >; typename QtPromisePrivate::PromiseDeduce<T>::Type = QtPromise::QPromise<QString>]::<lambda(const QtPromise::QPromiseResolve<QString>&, const QtPromise::QPromiseReject<QString>&)>; T = QString]', declared using local type 'QtPromise::qPromise(T&&) [with T = QFuture<QtPromise::QPromise<QString> >; typename QtPromisePrivate::PromiseDeduce<T>::Type = QtPromise::QPromise<QString>]::<lambda(const QtPromise::QPromiseResolve<QString>&, const QtPromise::QPromiseReject<QString>&)>', is used but never defined [-fpermissive]
QPromise(F&& resolver): QPromiseBase<T>(std::forward<F>(resolver)) { }
^~~~~~~~
In file included from /usr/include/qt/QtCore/qcoreapplication.h:46,
from /usr/include/qt/QtCore/QCoreApplication:1,
from /home/milian/projects/bugs/async/qpromise_kitchensink.cpp:1:
/usr/include/qt/QtCore/qobject.h:300:13: error: 'static typename std::enable_if<(QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1), QMetaObject::Connection>::type QObject::connect(const typename QtPrivate::FunctionPointer<Func>::Object*, Func1, Func2) [with Func1 = void (QFutureWatcherBase::*)(); Func2 = QtPromisePrivate::PromiseFulfill<QFuture<T> >::call(const QFuture<T>&, const QtPromise::QPromiseResolve<T>&, const QtPromise::QPromiseReject<T>&) [with T = QtPromise::QPromise<QString>]::<lambda()>; typename std::enable_if<(QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1), QMetaObject::Connection>::type = QMetaObject::Connection; typename QtPrivate::FunctionPointer<Func>::Object = QFutureWatcherBase]', declared using local type 'QtPromisePrivate::PromiseFulfill<QFuture<T> >::call(const QFuture<T>&, const QtPromise::QPromiseResolve<T>&, const QtPromise::QPromiseReject<T>&) [with T = QtPromise::QPromise<QString>]::<lambda()>', is used but never defined [-fpermissive]
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
^~~~~~~
make[2]: *** [CMakeFiles/qpromise_kitchensink.dir/build.make:63: CMakeFiles/qpromise_kitchensink.dir/qpromise_kitchensink.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:73: CMakeFiles/qpromise_kitchensink.dir/all] Error 2
The only way I could see to make it work, is by never using QtPromise from within the QtConcurrent callback, but only from within the .then()
continuation. So basically something like this will work:
qPromise(QtConcurrent::run([](){return QStringLiteral("foo");}))
.then([](const QString &arg) { return d(arg, arg); })
.then([](const QString &msg){ qDebug() << msg << "YEAH"; });
So, tl;dr: It's impossible to use QtPromise-enabled API from a QtConcurrent background task. Is this by design or could it be enabled?
It would be great if qPromise could somehow (un)wrap the QFuture<QtPromise::QPromise<T>>
into a QtPromise::QPromise<T>
. Then we could run QPromise
-enabled API directly with QtConcurrent.
I wanted to know if there exists an API similar to value_or(...)
that would allow me to do something like:
struct Something { int interesting{0}; };
int getter() {
QtPromise::QPromise<Something> promise = ...; // Obtained somehow
return promise.wait().value_or(Something{}).interesting;
}
I often find myself doing something similar to:
int getter() {
QtPromise::QPromise<Something> promise = ...;
int outerInteresting = 0;
promise.then([&](const Something& s) { outerInteresting = s.interesting; }.wait();
return outerInteresting;
}
Would an addition like value_or()
be useful for the library?
Btw. I was also thinking that to_optional()
could also fit my needs but that would require C++17 (and AFAIK the library aims to support C++11). What is the authors' current attitude towards conditional compilation and feature testing (if the compiler supports C++17)?
This is more of a question/clarification for the documentation, but is there an order of the results in a QPromise::all(...)
? Is it ordered as promises are resolved, or is it ordered as they are in the original sequence passed into QPromise::all(...)
?
Perhaps this is my misunderstanding of how the library works, but it seems there's a subtle difference of how .then() statements can be chained together:
Take the following example:
QtPromise::QPromise<QString> runShellCommand(const QString& command, const QStringList& arguments = {})
{
QProcess *process = new QProcess();
return QtPromise::QPromise<ProcessResult>([=](const auto& resolve, const auto& reject){
QObject::connect(process, &QProcess::finished, process, &QProcess::deleteLater);
QObject::connect(process, &QProcess::finished, [=](int exitCode, QProcess::ExitStatus exitStatus){
if( exitStatus != QProcess::NormalExit ) {
reject(exitStatus);
}
resolve(process->readAllStandardOutput());
});
qDebug() << "Running:" << command << arguments;
process->start(command, arguments);
});
}
QtPromise::QPromise<void> runScriptVersion1()
{
QtPromise::QPromise<void> p = QtPromise::attempt([this](){
return runShellCommand("ls")
.then([](const QString& output){
qDebug() << output;
});
});
p.then([](){
return runShellCommand("ls")
.then([](const QString& output){
qDebug() << output;
});
});
p.then([](){
return runShellCommand("pwd")
.then([](const QString& output){
qDebug() << output;
});
});
p.then([](){
return runShellCommand("ls")
.then([](const QString& output){
qDebug() << output;
});
});
return p;
}
QtPromise::QPromise<void> runScriptVersion2()
{
return QtPromise::QPromise<void> p = QtPromise::attempt([this](){
return runShellCommand("ls")
.then([](const QString& output){
qDebug() << "ls" << output;
});
})
.then([](){
return runShellCommand("ls")
.then([](const QString& output){
qDebug() << "pwd" << output;
});
})
.then([](){
return runShellCommand("pwd")
.then([](const QString& output){
qDebug() << "ls" << output;
});
})
.then([](){
return runShellCommand("ls")
.then([](const QString& output){
qDebug() << output;
});
});
}
Notice the different of how I chained together the .then() statements with and without ending the statement with ;
Version 1 I get the behavior of:
Running command: ls
Running command: pwd
Running command: ls
ls: <output>
pwd: <output>
ls: <output>
Version 2 I get the behavior of:
Running command: ls
ls: <output>
Running command: pwd
pwd: <output>
Running command: ls
ls: <output>
I would expect behavior 2 on both versions, but in the first version 3 processes get spawned, then all of the output. I'd like the second version as it's more script-like behavior.
The reason this matters to me is version 1 is actually contained within a loop, and I've effectively loop unrolled it. I figured I could "build" the .then() statements each loop iteration. Here's an example:
QtPromise::QPromise<void> runScript(const QStringList& commands)
{
QtPromise::QPromise<void> p = QtPromise::attempt([](){}); // Need this because there's no default constructor? Probably a better way to do this
for(const QString& command: commands) { // Assume commands = QStringList{"ls", "pwd", "ls"}
p.then([](){
return runShellCommand(command)
.then([](const QString& output){
qDebug() << output;
});
});
}
return p;
}
Let me know if there's just an overall better way to do this. Perhaps all I need is a .wait() at the end of the .then() statement in the loop? Also I wouldn't mind some guidance on how to do the promise structure better in the function without having to do the .attempt()
Thanks so much! This library has been so helpful already in other contexts for years now. :)
This library is a lifesaver. With JavaScript I use bluebirdjs
for promises and it has some very handy helpers for containers. When dealing with Promise::all(...)
post processing the returned sequence is a bit combersome. Example for filter:
QVector<QPromise<QByteArray> > promises{
download(QUrl("http://a...")),
download(QUrl("http://b...")),
download(QUrl("http://c..."))
};
QPromise<QByteArray>::all(promises)
.then([](const QVector<QByteArray>& res) {
QVector<QByteArray> out;
std::copy_if (res.begin(), res.end(), std::back_inserter(out), [](QByteArray a){
// return true/false based on some rule
});
return out;
})
.then([](const QVector<QByteArray>& res) {
// do something with it
});
What I would like to have is something like this:
QVector<QPromise<QByteArray>> promises{
download(QUrl("http://a...")),
download(QUrl("http://b...")),
download(QUrl("http://c..."))
};
QPromise<QByteArray>::all(promises)
.filter([](QByteArray a){
// return true/false based on some rule
}))
.then([](const QVector<QByteArray>& res) {
// do something with it
});
What would really be cool is to be able to do something like this:
QVector<QPromise<QByteArray>> promises{
download(QUrl("http://a...")),
download(QUrl("http://b...")),
download(QUrl("http://c..."))
};
QPromise<QByteArray>::all(promises)
.map([](QByteArray a) {
return QJsonDocument::fromJson(a).object();
})
.filter([](QJsonObject o){
return o['result'].toInt() == 200;
})
.then([](const QVector<QJsonObject >& res) {
// do something with it
});
I have a task that can be splitted into two steps, the first step is:
QPromise<Result1> step1(const QString &);
And the next step depends on the output of step1
, something like:
QPromise<void> step2(const Result1 &);
But the task cannot be implemented like this:
QPromise<void> task(const QString &s) {
// this is incorrect, return type is QPromise<QPromise<void>>
return step1(s).then(step2);
// also incorrect, you lose control of step2's promise
return step1(s).then([](const Result1 &r) {
step2(r);
});
}
So is there something can flatten nested promises?
I load my qt dll in a MFC app, and tried to use this library. But it kept reporting the warning as follows:
QCoreApplication::postEvent: Unexpected null receiver
Then I find it was triggered in qpromise_p.h
:
template<typename F>
static void qtpromise_defer(F&& f, const QPointer<QThread>& thread)
{
using FType = typename std::decay<F>::type;
struct Event : public QEvent
{
Event(FType&& f) : QEvent{QEvent::None}, m_f{std::move(f)} { }
Event(const FType& f) : QEvent{QEvent::None}, m_f{f} { }
~Event() override { m_f(); }
FType m_f;
};
if (!thread || thread->isFinished()) {
return;
}
QObject* target = QAbstractEventDispatcher::instance(thread);
if (!target && QCoreApplication::closingDown()) {
return;
}
Q_ASSERT_X(target, "postMetaCall", "Target thread must have an event loop");
QCoreApplication::postEvent(target, new Event{std::forward<F>(f)});
}
I guess the reason is I called the method in a non-qt thread, then QAbstractEventDispatcher::instance(thread)
return nullptr
.
Any suggestions?
So QList<T>
appears to be more demanding than expected and requires <T>
to be assignable as well, oddly enough only for certain build configurations, but still. Although the std
containers work perfectly fine, for full compatibility with the Qt ecosystem QPromise
requires operator=()
.
I actually think that's a good thing because it also allows for building promise chains at runtime without being forced into recursive patterns. E.g:
QPromise<void> p = getFirstPromise();
for (int i=0; i < nPromises; ++i)
p = p.then([]() { /* chain element */ });
return p;
So, copy and move assignment support for QPromise
? If move assignment is supported, shouldn't there be an "invalid/empty" state for QPromise
too? If there is an empty state, could there be a default constructor creating such a state and curiously bring us back to your other idea of solving the QVector
issue?
I get a compile error with GCC 9.1.0
qpromise_p.h:586:58: error: invalid use of incomplete type 'class QtPromise::QPromiseConversionException'
throw QtPromise::QPromiseConversionException{};
^
qpromise_p.h:34:7: note: forward declaration of 'class QtPromise::QPromiseConversionException'
class QPromiseConversionException;
qpromise_p.h is included by qpromise_exceptions.h which contains the full definition of QPromiseConversionException, so qpromise_p.h will always be read first.
qtpromise/src/qtpromise/qpromise_p.h
Line 62 in 21faa67
Above first you check if thread is nullptr
, then call it's isFinished()
method.
But according to Qt docs:
"QPointer<T>, behaves like a normal C++ pointer T *, except that it is automatically set to 0 when the referenced object is destroyed"
Meaning you hold only a weak reference,
and even if the thread was not null
just moments ago, it can suddenly be an invalid-pointer while isFinished()
did not yet return
(which hopefully causes a crash, instead of causing more damage).
What is the correct implementation to ensure network resources are freed when using QtPromise timeouts with QNetworkAccessManager? With my current code if a QPromiseTimeoutException happens, a QNetworkReply::finished signal is never generated so the network resources are not cleaned up.
I am using Qt 5.14.2 and cannot upgrade to QT 5.15 yet to take advantage of the new QNetworkRequest setTransferTimeout api call.
In my example code below, I attempt a network request to a nonexistent local url which triggers the QtPromise timeout.
Thanks!
main.cpp.txt
memoryprofiletest.cpp.txt
memoryprofiletest.h.txt
Hi,
first of all, thanks for the great library!
In the documentations there is an example of using a function pointer as an argument to then
:
download(url).then(&uncompress).then(
// lambda {...}
)
So I tried to apply the similar concept but using a member function instead:
download()
.then(std::bind(&MyAsyncClass::compress, this, std::placeholders::_1))
.then([](int result) { qDebug() << "Resolved:" << result; });
This can't be compiled with the following errors (MinGW 7.3.0):
In file included from C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/src/qtpromise/qpromise.h:25:0,
from C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/include/QtPromise:25,
from C:\devel\qtpromise_test\main.cpp:2:
C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/src/qtpromise/qpromise_p.h: In instantiation of 'struct QtPromisePrivate::PromiseHandler<QString, std::_Bind<QtPromise::QPromise<int> (MyAsyncClass::*(MyAsyncClass*, std::_Placeholder<1>))(QString)>, void>':
C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/src/qtpromise/qpromise.inl:85:1: required by substitution of 'template<class TFulfilled> typename QtPromisePrivate::PromiseHandler<QString, TFulfilled, typename QtPromisePrivate::ArgsOf<T>::first>::Promise QtPromise::QPromiseBase<QString>::then<TFulfilled>(TFulfilled&&) const [with TFulfilled = std::_Bind<QtPromise::QPromise<int> (MyAsyncClass::*(MyAsyncClass*, std::_Placeholder<1>))(QString)>]'
C:\devel\qtpromise_test\main.cpp:18:76: required from here
C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/src/qtpromise/qpromise_p.h:273:62: error: no type named 'type' in 'class std::result_of<std::_Bind<QtPromise::QPromise<int> (MyAsyncClass::*(MyAsyncClass*, std::_Placeholder<1>))(QString)>()>'
using ResType = typename std::result_of<THandler()>::type;
^
C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/src/qtpromise/qpromise_p.h:274:58: error: no type named 'type' in 'class std::result_of<std::_Bind<QtPromise::QPromise<int> (MyAsyncClass::*(MyAsyncClass*, std::_Placeholder<1>))(QString)>()>'
using Promise = typename PromiseDeduce<ResType>::Type;
^
C:\devel\qtpromise_test\main.cpp: In member function 'void MyAsyncClass::run()':
C:\devel\qtpromise_test\main.cpp:18:76: error: no matching function for call to 'QtPromise::QPromise<QString>::then(std::_Bind_helper<false, QtPromise::QPromise<int> (MyAsyncClass::*)(QString), MyAsyncClass*, const std::_Placeholder<1>&>::type)'
.then(std::bind(&MyAsyncClass::compress, this, std::placeholders::_1))
^
In file included from C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/include/QtPromise:25:0,
from C:\devel\qtpromise_test\main.cpp:2:
C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/src/qtpromise/qpromise.h:68:5: note: candidate: template<class TFulfilled, class TRejected> typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise QtPromise::QPromiseBase<T>::then(const TFulfilled&, const TRejected&) const [with TFulfilled = TFulfilled; TRejected = TRejected; T = QString]
then(const TFulfilled& fulfilled, const TRejected& rejected) const;
^~~~
C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/src/qtpromise/qpromise.h:68:5: note: template argument deduction/substitution failed:
C:\devel\qtpromise_test\main.cpp:18:76: note: candidate expects 2 arguments, 1 provided
.then(std::bind(&MyAsyncClass::compress, this, std::placeholders::_1))
^
In file included from C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/include/QtPromise:25:0,
from C:\devel\qtpromise_test\main.cpp:2:
C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/src/qtpromise/qpromise.h:72:5: note: candidate: template<class TFulfilled> typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise QtPromise::QPromiseBase<T>::then(TFulfilled&&) const [with TFulfilled = TFulfilled; T = QString]
then(TFulfilled&& fulfilled) const;
^~~~
C:/devel/builds/build-qtpromise_test-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/_deps/qtpromise-src/src/qtpromise/qpromise.h:72:5: note: substitution of deduced template arguments resulted in errors seen above
mingw32-make.exe[2]: *** [CMakeFiles\qtpromise_test.dir\build.make:77: CMakeFiles/qtpromise_test.dir/main.cpp.obj] Error 1
mingw32-make.exe[1]: *** [CMakeFiles\Makefile2:96: CMakeFiles/qtpromise_test.dir/all] Error 2
mingw32-make.exe: *** [Makefile:83: all] Error 2
14:01:48: The process "C:\Qt\Tools\CMake_64\bin\cmake.exe" exited with code 2.
Error while building/deploying project qtpromise_test (kit: Desktop Qt 5.13.2 MinGW 64-bit)
When executing step "CMake Build"
Assigning the std::bind
result to an std::function
doesn't work as well.
I know that I can use a lambda:
download()
.then([this](const QString &input) { return compress(input); })
.then([](int result) { qDebug() << "Resolved:" << result; });
But it would be also nice to use a std::bind
result or a std::function
.
I'm attaching a small .cpp file for you to test.
Environment:
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrl>
#include <QDebug>
#include <QVector>
#include <QtPromise>
#include <memory>
using namespace QtPromise;
using namespace std;
struct LaterDeleter {
template <class T>
void operator()(T *t) {
t->deleteLater();
}
};
using QNetworkReplyPtr = unique_ptr<QNetworkReply, LaterDeleter>;
QPromise<QNetworkReplyPtr> completed(QNetworkReply *reply) {
return QPromise<QNetworkReplyPtr>{ [reply](const auto &resolve, const auto &reject) {
QObject::connect(reply, &QNetworkReply::finished, [=]() {
QNetworkReplyPtr ptr{ reply };
if(ptr->error() == QNetworkReply::NoError)
resolve(move(ptr));
else
reject(ptr->error());
});
} };
}
int main() {
QNetworkAccessManager man;
QNetworkRequest example1(QUrl{"http://example1.com"});
auto promise1 = completed(man.get(example1)).then([](QNetworkReplyPtr reply) {
qDebug() << reply->header(QNetworkRequest::LocationHeader).toString();
});
QNetworkRequest example2(QUrl{"http://example2.com"});
auto promise2 = completed(man.get(example2)).then([](QNetworkReplyPtr reply) {
auto content = reply->readAll();
qDebug() << content;
});
QVector<QPromise<void>> promises{ move(promise1), move(promise2) };
QtPromise::all(promises).wait();
}
Because I need the Location
header of one of my http request, so I have to preserve QNetworkReply*
, and wrap it in unique_ptr
to automatically deleteLater()
. But promise's resolver is calling unique_ptr
's copy constructor, am I doing it wrong (like I should switch to shared_ptr
)?
Here is the build output:
[ 25%] Automatic MOC and UIC for target test
[ 25%] Built target test_autogen
[ 50%] Building CXX object CMakeFiles/test.dir/src/test.cpp.o
In file included from /home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise.h:4,
from /home/h/qa-qt/3rdparty/QtPromise/include/QtPromise:4,
from /home/h/qa-qt/src/test.cpp:7:
/home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise_p.h: In instantiation of 'static void QtPromisePrivate::PromiseDispatch<void>::call(const Resolve&, const Reject&, Functor, Args&& ...) [with Resolve = QtPromise::QPromiseResolve<void>; Reject = QtPromise::QPromiseReject<void>; Functor = main()::<lambda(QNetworkReplyPtr)>; Args = {const std::unique_ptr<QNetworkReply, LaterDeleter>&}]':
/home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise_p.h:244:43: required from 'static std::function<void(const T&)> QtPromisePrivate::PromiseHandler<T, THandler, TArg>::create(const THandler&, const TResolve&, const TReject&) [with TResolve = QtPromise::QPromiseResolve<void>; TReject = QtPromise::QPromiseReject<void>; T = std::unique_ptr<QNetworkReply, LaterDeleter>; THandler = main()::<lambda(QNetworkReplyPtr)>; TArg = std::unique_ptr<QNetworkReply, LaterDeleter>]'
/home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise.inl:50:62: required from 'typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise QtPromise::QPromiseBase<T>::then(const TFulfilled&, const TRejected&) const [with TFulfilled = main()::<lambda(QNetworkReplyPtr)>; TRejected = std::nullptr_t; T = std::unique_ptr<QNetworkReply, LaterDeleter>; typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise = QtPromise::QPromise<void>]'
/home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise.inl:66:61: required from 'typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise QtPromise::QPromiseBase<T>::then(TFulfilled&&) const [with TFulfilled = main()::<lambda(QNetworkReplyPtr)>; T = std::unique_ptr<QNetworkReply, LaterDeleter>; typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise = QtPromise::QPromise<void>]'
/home/h/qa-qt/src/test.cpp:41:6: required from here
/home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise_p.h:223:15: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = QNetworkReply; _Dp = LaterDeleter]'
223 | fn(std::forward<Args>(args)...);
| ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/9.2.0/memory:80,
from /usr/include/c++/9.2.0/thread:39,
from /usr/include/c++/9.2.0/future:39,
from /usr/include/qt/QtCore/qthread.h:50,
from /usr/include/qt/QtCore/QThread:1,
from /home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise_p.h:9,
from /home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise.h:4,
from /home/h/qa-qt/3rdparty/QtPromise/include/QtPromise:4,
from /home/h/qa-qt/src/test.cpp:7:
/usr/include/c++/9.2.0/bits/unique_ptr.h:406:7: note: declared here
406 | unique_ptr(const unique_ptr&) = delete;
| ^~~~~~~~~~
/home/h/qa-qt/src/test.cpp:39:55: note: initializing argument 1 of 'main()::<lambda(QNetworkReplyPtr)>'
39 | auto promise1 = completed(man.get(example1)).then([](QNetworkReplyPtr reply) {
| ^
In file included from /home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise.h:4,
from /home/h/qa-qt/3rdparty/QtPromise/include/QtPromise:4,
from /home/h/qa-qt/src/test.cpp:7:
/home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise_p.h: In instantiation of 'static void QtPromisePrivate::PromiseDispatch<void>::call(const Resolve&, const Reject&, Functor, Args&& ...) [with Resolve = QtPromise::QPromiseResolve<void>; Reject = QtPromise::QPromiseReject<void>; Functor = main()::<lambda(QNetworkReplyPtr)>; Args = {const std::unique_ptr<QNetworkReply, LaterDeleter>&}]':
/home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise_p.h:244:43: required from 'static std::function<void(const T&)> QtPromisePrivate::PromiseHandler<T, THandler, TArg>::create(const THandler&, const TResolve&, const TReject&) [with TResolve = QtPromise::QPromiseResolve<void>; TReject = QtPromise::QPromiseReject<void>; T = std::unique_ptr<QNetworkReply, LaterDeleter>; THandler = main()::<lambda(QNetworkReplyPtr)>; TArg = std::unique_ptr<QNetworkReply, LaterDeleter>]'
/home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise.inl:50:62: required from 'typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise QtPromise::QPromiseBase<T>::then(const TFulfilled&, const TRejected&) const [with TFulfilled = main()::<lambda(QNetworkReplyPtr)>; TRejected = std::nullptr_t; T = std::unique_ptr<QNetworkReply, LaterDeleter>; typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise = QtPromise::QPromise<void>]'
/home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise.inl:66:61: required from 'typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise QtPromise::QPromiseBase<T>::then(TFulfilled&&) const [with TFulfilled = main()::<lambda(QNetworkReplyPtr)>; T = std::unique_ptr<QNetworkReply, LaterDeleter>; typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise = QtPromise::QPromise<void>]'
/home/h/qa-qt/src/test.cpp:47:6: required from here
/home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise_p.h:223:15: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = QNetworkReply; _Dp = LaterDeleter]'
223 | fn(std::forward<Args>(args)...);
| ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/9.2.0/memory:80,
from /usr/include/c++/9.2.0/thread:39,
from /usr/include/c++/9.2.0/future:39,
from /usr/include/qt/QtCore/qthread.h:50,
from /usr/include/qt/QtCore/QThread:1,
from /home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise_p.h:9,
from /home/h/qa-qt/3rdparty/QtPromise/include/../src/qtpromise/qpromise.h:4,
from /home/h/qa-qt/3rdparty/QtPromise/include/QtPromise:4,
from /home/h/qa-qt/src/test.cpp:7:
/usr/include/c++/9.2.0/bits/unique_ptr.h:406:7: note: declared here
406 | unique_ptr(const unique_ptr&) = delete;
| ^~~~~~~~~~
/home/h/qa-qt/src/test.cpp:44:55: note: initializing argument 1 of 'main()::<lambda(QNetworkReplyPtr)>'
44 | auto promise2 = completed(man.get(example2)).then([](QNetworkReplyPtr reply) {
| ^
make[2]: *** [CMakeFiles/test.dir/build.make:76: CMakeFiles/test.dir/src/test.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:78: CMakeFiles/test.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
When I close window I want to cancel all pending promises, but cannot figure how can do this?
Hi !
First: thanks for your beautiful work on this.
Is there any plan to support QPromise exposition to QML ?
I'm having trouble using QPromise::all
on a list of promises that is not determined at compile time. If I'm getting this right, this isn't possible at the moment due to the following:
QPromise::all
only accepts QVector<QPromise>
as inputQVector<T>
does not support adding a T
or converting from std::vector<T>
if T
has no default constructorQPromise
has no default constructorSo it seems that there is no way to build a QVector<QPromise>
other than using static compile-time initializer lists (like documentation and tests are doing it). I can create a std::vector<QPromise>
using std::vector::emplace
just fine, but Qt can't even convert from that without a default constructor.
Could this limitation be solved in some way? Would it be better to make QPromise
default constructible or to support std::vector
in QPromise::all
?
"Getting Started" section, for the CMake (Fetch Content) part (here) indicates that the tag
to be used is origin/0.6.0
.
This version, however, was not yet released.
So, my next step was to use the one last released (origin/0.5.0
). This didn't work because the origin
prefix was unnecessary. Standalone 0.5.0
turned out not to have proper CMake support. Only after switching to master
it finally worked.
I'd suggest to improve the documentation to reflect the usage.
Otherwise, great work! Thanks!
Hello, I recently discovered this project and I'm amazed by it. I'm planning to start using QPromise in my own project. But Qt 6 is coming and has a new class named QPromise. Do you plan to change the class name? Do you plan to support Qt 6 at all?
I use qtpromise
a lot in my project and I am constantly running into next situation
static QtPromise::QPromise<void> DoSomething()
{
return QtPromise::resolve().delay(5000);
}
////
struct Foo : public QObject {
void doSomethingAndDoSomethingElse()
{
QPointer<Foo> self(this);
DoSomething().then([self] {
if(self) {
// do something with self.
qDebug() << "Self is ok";
}
else {
qDebug() << "Foo already deleted";
}
});
}
};
Foo *foo = new Foo;
foo->doSomethingAndDoSomethingElse();
// here foo gets deleted(by deleteLater for instance)
So it can happen that Foo gets deleted before promise is resolved, and since function is called as callback I need to check if this
is still valid in some way otherwise I get segfault.
I was thinking a lot about it and it seems like a pretty common thing.
I see 3 ways how to resolve this situation.
this
as I am doing it right now.thenVia(QObject *, callback)
or something like then(callback).via(QObject*)
which will be used as context for delivering callback. If such functionality exists then my use case will be resolved just by using context for continuations since if context is already deleted, callback won't be executed(same behavior with slots). Implementation wise it shouldn't be that hard to do it, since it requires just some modifications to qtpromise_defer
.Looking forward to discussing it
Avoid multiple definition of XXX, use static inline
may be better.
These days i saw your code, i want to ask why not to use std::enable_if to avoid use Promise and PromiseBase ,or other classes?
In contrast to the QPromise::reject
promise that is being tested in tst_fail
, regular non-void promises fail to compile .fail
methods being attached directly to p
:
auto p = QPromise<int>([&](
const QPromiseResolve<int>& resolve,
const QPromiseReject<int>& reject)
{
resolve(42);
reject(43);
});
p.fail([](int x) {
Q_UNUSED(x);
});
...qtpromise/src/qtpromise/qpromise_p.h:604: error: no matching function for call to ‘QtPromisePrivate::PromiseData<int>::resolve()’
promise->m_d->resolve();
^~~~~~~
If .fail
is preceded by a dummy .then
method, the fail works:
p.then([](){}).fail([](int x) {
Q_UNUSED(x);
});
QPromise::reject(QString{"foo"})
.then([&](const int& res) {
qDebug() << res;
return QPromise::resolve("l1-resolve");
},
[&](const QString& txt) {
qDebug() << txt;
return QPromise::reject("l1-reject");
})
.then([](const QString& txt){
qDebug() << "l2-resolve" << txt;
},[](const QString& txt){
qDebug() << "l2-reject" << txt;
});
Hi...
I'm using QtPromise but encountered a problem...I need a way to unpack QPromise and then assign the value to a private member of a class...But I can't do that because THE this pointer can't be accessed in the then of qtpromise facility...Is there anyone can't tell me how to do that...Thank you...
After including the headers, a lot of QtPromise-specific warnings are shown on compilation.
I've been able to suppress them by adding the following pragmas to "QtPromise":
#ifndef QTPROMISE_MODULE_H
#define QTPROMISE_MODULE_H
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-local-typedef"
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include "../src/qtpromise/qpromise.h"
#include "../src/qtpromise/qpromisefuture.h"
#include "../src/qtpromise/qpromisehelpers.h"
#pragma GCC diagnostic pop
#endif // ifndef QTPROMISE_MODULE_H
It would be nice to not have to do this.
Thanks for the great library.
#include <QtPromise>
using namespace QtPromise;
typedef QSharedPointer< QNetworkReply > NetworkReplyPtr;
QPromise< NetworkReplyPtr > download(const QUrl& url)
{
return QPromise<NetworkReplyPtr>( [=](
const QPromiseResolve< NetworkReplyPtr >& resolve,
const QPromiseReject< NetworkReplyPtr >& reject) {
QNetworkReply* reply = NVHttpManager::ptr()->get( QNetworkRequest(url) );
QObject::connect( reply, &QNetworkReply::destroyed, []() {
qDebug() << "reply destroyed";
} );
QObject::connect( reply, &QNetworkReply::finished, [=]() {
if( reply->error() == QNetworkReply::NoError ) {
resolve( NetworkReplyPtr( reply, &QNetworkReply::deleteLater ) );
} else {
reject( NetworkReplyPtr( reply, &QNetworkReply::deleteLater ) );
}
});
});
}
int main() {
download( QUrl( "http://www.google.com" ) )
.then( []( NetworkReplyPtr result ) {
qDebug() << result->readAll();
return result;
} )
}
its has error in vs2015
C2668: ambiguous call to overloaded function [duplicate]
So I switched to shared_ptr.
typedef std::shared_ptr< QNetworkReply > NetworkReplyPtr;
its compile is done. but not delete QNetworkReply.
resolve( NetworkReplyPtr( reply, std::bind( &QNetworkReply::deleteLater, reply ) ) );
reject( NetworkReplyPtr( reply, std::bind( &QNetworkReply::deleteLater, reply ) ) );
////
download( QUrl( "http://www.google.com" ) )
.then( []( NetworkReplyPtr result ) {
qDebug() << result->readAll();
return result;
} )
.finally( []( NetworkReplyPtr result ) {
qDebug() << result.use_count(); // output 3. not destroy.
} );
I want to use QSharedPointer to destroy it when QPromise is done.
Please help me.
Since we are talking "Bluebird" I wonder how hard it would be to implement Promise.prop
from bluebird. I would envision this to operate similarly to QPromise::all
but it would take a map instead of a sequence. I am not sure how this would translate since we cannot construct classes in C++, but what if we just used a QMap<QString, T>
? An example of how this might look would be:
QMap<QString, QPromise<QByteArray>> servers({
{"a", download(QUrl("http://a..."))},
{"b", download(QUrl("http://b..."))},
{"c", download(QUrl("http://c..."))}
});
QPromise::prop(servers)
.then([](QMap<QString, QByteArray> res) {
qDebug() << res["a"] << res["b"] << res["c"];
});
Hello, I noticed in the documentation there's references to a v0.7.0, but no such tag exists currently. Could we get a semi-stable release of v0.7.0 that has the Qt6 support within it, or are there more features to be planned for the official v0.7.0 release?
Thanks!
In the small test programm below, neither fail nor finally is executed.
#include <QByteArray>
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QtPromise>
#include <iostream>
struct Consumer
{
Consumer( QNetworkAccessManager* manager )
: m_manager( manager )
{
}
QtPromise::QPromise<QByteArray> get( const QUrl& url )
{
return QtPromise::QPromise<QByteArray>{ [&]( const QtPromise::QPromiseResolve<QByteArray>& resolve,
const QtPromise::QPromiseReject<QByteArray>& reject ) {
QNetworkReply* reply = m_manager->get( QNetworkRequest{ url } );
QObject::connect( reply, &QNetworkReply::finished, [=]() {
if( reply->networkError() == QNetworkReply::NoError )
{
qDebug() << "ONE DOWNLOADED: " << url;
resolve( reply->readAll() );
}
else
{
qDebug() << "ONE FAILED: " << url << reply->networkError();
reject( reply->errorString() );
}
reply->deleteLater();
} );
} };
}
QNetworkAccessManager* m_manager;
};
int main( int argc, char** argv )
{
QCoreApplication app{ argc, argv };
QNetworkAccessManager manager;
QVector<QtPromise::QPromise<QByteArray>> filesToDownload{
Consumer( &manager ).get( QUrl( "https://github.com/simonbrunel/qtpromise/blob/master/README.md" ) ),
Consumer( &manager ).get( QUrl( "https://github.com/simonbrunel/qtpromise/blob/master/LICENSE" ) ),
Consumer( &manager ).get( QUrl( "https://github.com/simonbrunel/qtpromise/blob/master/HURZ" ) ),
};
auto allDownloads = QtPromise::all( filesToDownload );
allDownloads
.then( []( const QVector<QByteArray>& downloadedFiles ) {
for( const auto& data : downloadedFiles )
{
qDebug() << data.size() << ": " << data.left( 25 );
}
} )
.fail( []( std::pair<QUrl, QNetworkReply::NetworkError> error ) {
qDebug() << "FAILED: " << error.second << "(" << error.first << ")";
} )
.fail( []( QNetworkReply::NetworkError error ) { qDebug() << "FAILED: " << error; } )
.fail( []( QString error ) { qDebug() << "FAILED: " << error; } )
.fail( []() { std::cerr << "FAILED" << std::endl; } )
.finally( []() { qDebug() << "FINALLY"; } );
allDownloads.wait();
qDebug() << "WAIT";
}
@simonbrunel Hi! How attached are you to the minimum required Qt version? Any particular reason for choosing 5.4 instead of, say version 5.6 (LTS and oldest version still supported by Qt)? The reason I'm asking is because I'm having trouble making my unit tests pass on Travis due to Qt bugs present in 5.4, which are fixed in 5.6. Appveyor tests already use 5.6 as minimum version and are doing fine.
Other than that I also had to upgrade the GCC on Travis from 4.8 to 4.9, also due to a bug in the old version. The tests still comply to the minimum requirement of using C++11, so this is fine I guess?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.