Page MenuHomePhorge

qt-promise-handler.hpp
No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None

qt-promise-handler.hpp

/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include <future>
#include <QObject>
#include <QMetaObject>
#include <QPointer>
#include <QTimer>
#include <promise-interface.hpp>
template<class T>
class QtPromise;
namespace QtPromiseDetail
{
struct QtPromiseHelper
{
template<class T>
using PromiseType = QtPromise<T>;
};
struct IdentityFunc
{
template<class T>
constexpr T &&operator()(T &&t) const {
return std::forward<T>(t);
}
};
template<class Func>
void post(QObject *obj, Func &&func)
{
QMetaObject::invokeMethod(obj, std::forward<Func>(func), Qt::QueuedConnection);
}
}
class QtPromiseSignalTrigger : public QObject
{
Q_OBJECT
public:
QtPromiseSignalTrigger(QObject *parent = 0);
~QtPromiseSignalTrigger();
Q_SIGNALS:
void finished();
};
template<class T>
class QtPromise : public Kazv::AbstractPromise<QtPromiseDetail::QtPromiseHelper::PromiseType, T>
{
using BaseT = Kazv::AbstractPromise<QtPromiseDetail::QtPromiseHelper::PromiseType, T>;
template<class FuncT, class PromiseT, class ResolveT>
struct WaitHelper
{
void resolveOrWait()
{
auto res = func(p.get());
using ResT = decltype(res);
if constexpr (Kazv::isPromise<ResT>) {
res.then([resolve=resolve](auto val) {
resolve(std::move(val));
return std::decay_t<decltype(val)>(); // we don't care about this value
});
} else {
resolve(res);
}
}
void wait()
{
if (p.ready()) {
QtPromiseDetail::post(
executor,
[*this]() mutable { resolveOrWait(); }
);
} else {
QObject::connect(
p.m_signalTrigger, &QtPromiseSignalTrigger::finished,
executor, [*this]() mutable {
resolveOrWait();
}
);
}
}
QPointer<QObject> executor;
PromiseT p;
FuncT func;
ResolveT resolve;
};
struct ResolveHelper
{
template<class ValT>
void operator()(ValT val) const
{
using ResT = std::decay_t<decltype(val)>;
if constexpr (Kazv::isPromise<ResT>) {
QtPromiseDetail::post(
executor,
[qtPromise=std::move(val), *this]() mutable {
if (qtPromise.ready()) {
setValue(qtPromise.get());
} else {
qtPromise.then([*this](auto val) mutable {
setValue(std::move(val));
return std::decay_t<decltype(val)>(); // we don't care about this value
});
}
});
} else {
QtPromiseDetail::post(
executor,
[*this, val=std::move(val)]() mutable {
setValue(std::move(val));
});
}
}
void setValue(T resolvedValue) const
{
p->set_value(std::move(resolvedValue));
if (signalTrigger) {
Q_EMIT signalTrigger->finished();
signalTrigger->deleteLater();
}
}
QPointer<QObject> executor;
QPointer<QtPromiseSignalTrigger> signalTrigger;
std::shared_ptr<std::promise<T>> p;
};
public:
QtPromise(QObject *executor, T value)
: BaseT(this)
, m_executor(executor)
, m_signalTrigger()
{
std::promise<T> p;
m_val = p.get_future().share();
p.set_value(std::move(value));
}
template<class Func>
QtPromise(QObject *executor, Func &&callback)
: BaseT(this)
, m_executor(executor)
, m_signalTrigger(new QtPromiseSignalTrigger())
{
auto p = std::make_shared<std::promise<T>>();
m_val = p->get_future().share();
m_signalTrigger->moveToThread(m_executor->thread());
auto resolve = ResolveHelper{m_executor, m_signalTrigger, p};
QtPromiseDetail::post(
m_executor,
[=, callback=std::forward<Func>(callback),
resolve=std::move(resolve)]() {
callback(resolve);
});
}
// FuncT: (DataT) -> AnotherDataT
// where AnotherDataT = PromiseThenResult<FuncT, typename BaseT::DataT>
template<class FuncT>
auto then(FuncT &&func)
-> QtPromise<Kazv::PromiseThenResult<FuncT, typename BaseT::DataT>> {
return QtPromise<Kazv::PromiseThenResult<FuncT, typename BaseT::DataT>>(
m_executor,
[=, func=std::forward<FuncT>(func), *this](auto resolve) {
auto waitHelper = WaitHelper<std::decay_t<FuncT>,
QtPromise,
std::decay_t<decltype(resolve)>>{
m_executor,
*this,
func,
resolve
};
waitHelper.wait();
});
}
bool ready() const {
return m_val.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
}
T get() const {
return m_val.get();
}
private:
QPointer<QObject> m_executor;
QPointer<QtPromiseSignalTrigger> m_signalTrigger;
std::shared_future<T> m_val;
};
class QtPromiseHandler : public Kazv::PromiseInterface<QtPromiseHandler,
QtPromiseDetail::QtPromiseHelper::PromiseType>
{
using BaseT = Kazv::PromiseInterface<QtPromiseHandler,
QtPromiseDetail::QtPromiseHelper::PromiseType>;
public:
template<class T>
using PromiseT = QtPromise<T>;
QtPromiseHandler(std::reference_wrapper<QObject> executor)
: BaseT(this)
, m_executor(&executor.get())
{}
QtPromiseHandler(const QtPromiseHandler &that)
: BaseT(this)
, m_executor(that.m_executor)
{}
QtPromiseHandler(QtPromiseHandler &&that)
: BaseT(this)
, m_executor(std::move(that.m_executor))
{}
QtPromiseHandler &operator=(const QtPromiseHandler &that) {
m_executor = that.m_executor;
return *this;
}
QtPromiseHandler &operator=(QtPromiseHandler &&that) {
m_executor = std::move(that.m_executor);
return *this;
}
template<class T, class FuncT>
PromiseT<T> create(FuncT &&func) {
return PromiseT<T>(m_executor, std::forward<FuncT>(func));
}
template<class T>
PromiseT<T> createResolved(T val) {
return PromiseT<T>(m_executor, std::move(val));
}
private:
QPointer<QObject> m_executor;
};

File Metadata

Mime Type
text/x-c++
Expires
Sun, Dec 1, 9:20 AM (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41645
Default Alt Text
qt-promise-handler.hpp (7 KB)

Event Timeline