Commit 18a3dd7c authored by Linus Jahn's avatar Linus Jahn 🔌
Browse files

Add await functions for handling QFutures (from other threads)

This adds two await functions:
1. Just attaches a QFutureWatcher to a given QFuture and executes a
   given lambda with the future result.
2. Executes a lambda on another thread (using a run() function) and
   handles the returned QFuture (from the other thread) on the caller
   thread.

Example for the simple case:
await(discoveryManager->requestDiscoInfo("groups.kaidan.im"), this,
      [](QXmppDiscoveryManager::InfoResult &&result) {
	if (const auto info = std::get_if<QXmppDiscoveryIq>(&result)) {
		qDebug() << info->features();
	} else {
		qDebug() << std::get<QXmppStanza::Error>(result).text();
	}
});

Example usage for the advanced case:
await(m_client, [=] {
	// XMPP thread (m_client)
	return discoveryManager->requestDiscoInfo("groups.kaidan.im");
}, this, [](QXmppDiscoveryManager::InfoResult &&result) {
	// caller thread (this)
	if (const auto info = std::get_if<QXmppDiscoveryIq>(&result)) {
		qDebug() << info->features();
	} else {
		qDebug() << std::get<QXmppStanza::Error>(result).text();
	}
});

m_client needs to have a run(std::function) function, that can be added
using the RUN_FUNCTION() macro.

Currently this is unused, but we're going to need it as soon as we start
to use the new QFuture APIs from QXmpp.
parent af1b2d99
Pipeline #78620 passed with stage
in 2 minutes and 43 seconds
......@@ -30,11 +30,13 @@
#pragma once
#include <functional>
// Qt
// QXmpp
#include <QXmppClient.h>
// Kaidan
#include "Enums.h"
#include "Globals.h"
class AccountManager;
class AvatarFileStorage;
......@@ -61,6 +63,7 @@ class Settings;
class ClientWorker : public QObject
{
Q_OBJECT
RUN_FUNCTION()
Q_PROPERTY(RegistrationManager* registrationManager READ registrationManager CONSTANT)
Q_PROPERTY(VCardManager* vCardManager READ vCardManager CONSTANT)
......
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2021 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QFuture>
#include <QFutureWatcher>
template<typename T, typename Handler>
void await(const QFuture<T> &future, QObject *context, Handler handler)
{
auto *watcher = new QFutureWatcher<T>(context);
QObject::connect(watcher, &QFutureWatcherBase::finished,
context, [watcher, handler { std::move(handler) }]() {
handler(watcher->result());
watcher->deleteLater();
});
watcher->setFuture(future);
}
template<typename Runner, typename Functor, typename Handler>
void await(Runner *runner, Functor function, QObject *context, Handler handler)
{
// executor() must return a QFuture<T>, executor().result() gives T
using Result = decltype(function().result());
auto *watcher = new QFutureWatcher<Result>(context);
QObject::connect(watcher, &QFutureWatcherBase::finished,
context, [watcher, handler { std::move(handler) }]() {
handler(watcher->result());
watcher->deleteLater();
});
runner->run([watcher, function { std::move(function) }]() {
watcher->setFuture(function());
});
}
......@@ -107,4 +107,17 @@ constexpr auto SERVER_LIST_MIN_SERVERS_FROM_COUNTRY = 2;
// JPEG export quality used when saving images lossy (e.g. when saving images from clipboard)
constexpr auto JPEG_EXPORT_QUALITY = 85;
// Defines run function to run any code on the object's thread
#define RUN_FUNCTION() \
public: \
void run(std::function<void()> &&function) \
{ \
QMetaObject::invokeMethod(this, "_run", Q_ARG(std::function<void()>, std::move(function))); \
} \
private: \
Q_INVOKABLE void _run(const std::function<void()> &function) \
{ \
function(); \
}
#endif // GLOBALS_H
......@@ -99,6 +99,7 @@ Q_DECLARE_METATYPE(QXmppResultSetReply);
Q_DECLARE_METATYPE(QXmppVCardIq)
Q_DECLARE_METATYPE(QXmppVersionIq)
Q_DECLARE_METATYPE(std::function<void()>)
Q_DECLARE_METATYPE(std::function<void(RosterItem&)>)
Q_DECLARE_METATYPE(std::function<void(Message&)>)
......@@ -252,6 +253,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
qRegisterMetaType<QVector<Message>>();
qRegisterMetaType<QVector<RosterItem>>();
qRegisterMetaType<QHash<QString,RosterItem>>();
qRegisterMetaType<std::function<void()>>();
qRegisterMetaType<std::function<void(RosterItem&)>>();
qRegisterMetaType<std::function<void(Message&)>>();
qRegisterMetaType<QXmppVCardIq>();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment