Loading...
Searching...
No Matches
QXmppTask.h
// SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im>
// SPDX-FileCopyrightText: 2022 Jonah Brüchert <jbb@kaidan.im>
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#ifndef QXMPPTASK_H
#define QXMPPTASK_H
#include "qxmpp_export.h"
#include <functional>
#include <memory>
#include <optional>
#include <QFuture>
#include <QPointer>
template<typename T>
class QXmppPromise;
namespace QXmpp::Private {
struct TaskData;
class QXMPP_EXPORT TaskPrivate
{
public:
TaskPrivate(void (*freeResult)(void *));
~TaskPrivate();
bool isFinished() const;
void setFinished(bool);
bool isContextAlive();
void setContext(const QObject *);
void *result() const;
void setResult(void *);
void resetResult() { setResult(nullptr); }
const std::function<void(TaskPrivate &, void *)> continuation() const;
void setContinuation(std::function<void(TaskPrivate &, void *)> &&);
void invokeContinuation(void *result);
private:
std::shared_ptr<TaskData> d;
};
} // namespace QXmpp::Private
template<typename T>
class QXmppTask
{
public:
~QXmppTask() = default;
#ifndef QXMPP_DOC
template<typename Continuation>
#endif
void then(const QObject *context, Continuation continuation)
{
if constexpr (!std::is_void_v<T>) {
static_assert(std::is_invocable_v<Continuation, T &&>, "Function needs to be invocable with T &&.");
} else {
static_assert(std::is_invocable_v<Continuation>, "Function needs to be invocable without arguments.");
}
using namespace QXmpp::Private;
if (d.isFinished()) {
if constexpr (std::is_void_v<T>) {
continuation();
} else {
// when calling then() after finished value could be empty
if (hasResult()) {
continuation(std::move(*reinterpret_cast<T *>(d.result())));
d.resetResult();
}
}
} else {
d.setContext(context);
d.setContinuation([f = std::forward<Continuation>(continuation)](TaskPrivate &d, void *result) mutable {
if (d.isContextAlive()) {
if constexpr (std::is_void_v<T>) {
f();
} else {
f(std::move(*reinterpret_cast<T *>(result)));
}
}
// clear continuation to avoid "deadlocks" in case the user captured this QXmppTask
d.setContinuation({});
});
}
}
[[nodiscard]] bool isFinished() const { return d.isFinished(); }
#ifndef QXMPP_DOC
template<typename U = T, std::enable_if_t<(!std::is_void_v<U>)> * = nullptr>
#endif
[[nodiscard]] bool hasResult() const
{
return d.result() != nullptr;
}
#ifdef QXMPP_DOC
[[nodiscard]] const T &result() const
#else
template<typename U = T, std::enable_if_t<(!std::is_void_v<U>)> * = nullptr>
[[nodiscard]] const U &result() const
#endif
{
Q_ASSERT(isFinished());
Q_ASSERT(hasResult());
return *reinterpret_cast<U *>(d.result());
}
#ifdef QXMPP_DOC
[[nodiscard]] T takeResult()
#else
template<typename U = T, std::enable_if_t<(!std::is_void_v<U>)> * = nullptr>
[[nodiscard]] U takeResult()
#endif
{
Q_ASSERT(isFinished());
Q_ASSERT(hasResult());
U result = std::move(*reinterpret_cast<U *>(d.result()));
d.resetResult();
return result;
}
QFuture<T> toFuture(const QObject *context)
{
QFutureInterface<T> interface;
if constexpr (std::is_same_v<T, void>) {
then(context, [interface]() mutable {
interface.reportFinished();
});
} else {
then(context, [interface](T &&val) mutable {
interface.reportResult(val);
interface.reportFinished();
});
}
return interface.future();
}
private:
friend class QXmppPromise<T>;
explicit QXmppTask(QXmpp::Private::TaskPrivate data)
: d(std::move(data))
{
}
QXmpp::Private::TaskPrivate d;
};
#endif // QXMPPTASK_H
QXmppPromise
Create and update QXmppTask objects to communicate results of asynchronous operations.
Definition QXmppPromise.h:23
QXmppTask
Definition QXmppTask.h:62
QXmppTask::hasResult
bool hasResult() const
Definition QXmppTask.h:151
QXmppTask::result
const T & result() const
Definition QXmppTask.h:162
QXmppTask::toFuture
QFuture< T > toFuture(const QObject *context)
Definition QXmppTask.h:195
QXmppTask::isFinished
bool isFinished() const
Definition QXmppTask.h:143
QXmppTask::takeResult
T takeResult()
Definition QXmppTask.h:179
QXmppTask::then
void then(const QObject *context, Continuation continuation)
Definition QXmppTask.h:101