Page MenuHomePhorge

No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None
diff --git a/src/qt-promise-handler.hpp b/src/qt-promise-handler.hpp
new file mode 100644
index 0000000..b12266f
--- /dev/null
+++ b/src/qt-promise-handler.hpp
@@ -0,0 +1,210 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2020-2021 Tusooa Zhu <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libkazv-config.hpp>
+
+#include <future>
+
+#include <QObject>
+#include <QMetaObject>
+
+#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(std::reference_wrapper<QObject> obj, Func &&func)
+ {
+ QMetaObject::invokeMethod(&obj.get(), std::forward<Func>(func), Qt::QueuedConnection);
+ }
+}
+
+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 wait() const {
+ if (p.ready()) {
+ auto res = func(p.get());
+ using ResT = decltype(res);
+ if constexpr (Kazv::isPromise<ResT>) {
+ QtPromiseDetail::post(
+ executor,
+ [w=WaitHelper<QtPromiseDetail::IdentityFunc, ResT, ResolveT>{
+ executor,
+ res,
+ QtPromiseDetail::IdentityFunc{},
+ resolve,
+ }]() mutable {
+ w.wait();
+ });
+ } else {
+ resolve(res);
+ }
+ } else {
+ QtPromiseDetail::post(
+ executor,
+ [*this]() mutable {
+ wait();
+ }
+ );
+ }
+ }
+
+ std::reference_wrapper<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>) {
+ auto w = WaitHelper<QtPromiseDetail::IdentityFunc, ResT, ResolveHelper>{
+ executor,
+ val,
+ QtPromiseDetail::IdentityFunc{},
+ *this
+ };
+ w.wait();
+ } else {
+ p->set_value(std::move(val));
+ }
+ }
+
+ std::reference_wrapper<QObject> executor;
+ std::shared_ptr<std::promise<T>> p;
+ };
+public:
+ QtPromise(std::reference_wrapper<QObject> executor, T value)
+ : BaseT(this)
+ , m_executor(std::move(executor)) {
+ std::promise<T> p;
+ m_val = p.get_future().share();
+ p.set_value(std::move(value));
+ }
+
+ template<class Func>
+ QtPromise(std::reference_wrapper<QObject> executor, Func &&callback)
+ : BaseT(this)
+ , m_executor(std::move(executor)) {
+ auto p = std::make_shared<std::promise<T>>();
+ m_val = p->get_future().share();
+ auto resolve = ResolveHelper{m_executor, 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:
+ std::reference_wrapper<QObject> m_executor;
+ 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)
+ {}
+
+ 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:
+ std::reference_wrapper<QObject> m_executor;
+};
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 6015c55..fe6caa1 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,4 +1,5 @@
ecm_add_tests(
qt-job-handler-test.cpp
+ qt-promise-handler-test.cpp
LINK_LIBRARIES Qt5::Test libkazv::kazvall kazvprivlib
)
diff --git a/src/tests/qt-promise-handler-test.cpp b/src/tests/qt-promise-handler-test.cpp
new file mode 100644
index 0000000..d989c3d
--- /dev/null
+++ b/src/tests/qt-promise-handler-test.cpp
@@ -0,0 +1,109 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2020-2021 Tusooa Zhu <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libkazv-config.hpp>
+
+#include <memory>
+#include <vector>
+
+#include <QTimer>
+
+#include "qt-promise-handler-test.hpp"
+
+#include "qt-promise-handler.hpp"
+
+namespace
+{
+ struct MockDataStruct
+ {
+ int i{};
+ };
+}
+
+void QtPromiseHandlerTest::testPromise()
+{
+ QEventLoop loop;
+ QObject *obj = new QObject(&loop);
+ auto ph = QtPromiseHandler(std::ref(*obj));
+
+ std::vector<int> v;
+
+ auto p1 = ph.create<int>([&v](auto resolve) {
+ qDebug() << "p1";
+ v.push_back(1);
+ resolve(2);
+ });
+
+ auto p2 = p1.then([&v, &ph](int val) {
+ qDebug() << "p2";
+ v.push_back(val);
+ return ph.createResolved(3);
+ });
+
+ auto p3 = p2.then([&v, &ph](int val) {
+ qDebug() << "p3";
+ v.push_back(val);
+ return ph.createResolved(-1);
+ });
+
+ auto p4 = p3.then([](int val) {
+ qDebug() << "p4" << val;
+ [=] { QVERIFY(val == -1); }();
+ return 5;
+ });
+
+ auto p5 = p4.then([](int val) {
+ qDebug() << "p5" << val;
+ [=] { QVERIFY(val == 5); }();
+ return MockDataStruct{6};
+ });
+
+ auto p6 = p5.then([obj, &loop](MockDataStruct m) {
+ qDebug() << "p6" << m.i;
+ [=] { QVERIFY(m.i == 6); }();
+ obj->deleteLater();
+ loop.quit();
+ return 0;
+ });
+
+ QObject::connect(obj, &QObject::destroyed,
+ this, [&v] {
+ qDebug() << "QObject::destroyed()";
+ QVERIFY((v == std::vector<int>{ 1, 2, 3 }));
+ });
+
+ loop.exec();
+}
+
+void QtPromiseHandlerTest::testTimer()
+{
+ QEventLoop loop;
+ QObject *obj = new QObject(&loop);
+ auto ph = QtPromiseHandler(std::ref(*obj));
+
+ auto pTimer = ph.create<int>([](auto resolve) {
+ QTimer::singleShot(300, [resolve]() { resolve(20); });
+ });
+
+ int i;
+
+ auto pTimer2 = pTimer.then([&i, &loop, obj](int val) -> int {
+ i = val;
+ obj->deleteLater();
+ loop.quit();
+ return 0;
+ });
+
+ QObject::connect(obj, &QObject::destroyed,
+ this, [&i] {
+ qDebug() << "QObject::destroyed()";
+ QVERIFY(i == 20);
+ });
+
+ loop.exec();
+}
+
+QTEST_MAIN(QtPromiseHandlerTest)
diff --git a/src/tests/qt-promise-handler-test.hpp b/src/tests/qt-promise-handler-test.hpp
new file mode 100644
index 0000000..239f4a4
--- /dev/null
+++ b/src/tests/qt-promise-handler-test.hpp
@@ -0,0 +1,18 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2020-2021 Tusooa Zhu <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <QtTest>
+
+class QtPromiseHandlerTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testPromise();
+ void testTimer();
+};

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 12:34 PM (7 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55165
Default Alt Text
(9 KB)

Event Timeline