add promise code.

This commit is contained in:
amass 2024-09-15 16:15:26 +08:00
parent fe89ecd0f8
commit c82f079bcb
10 changed files with 1005 additions and 4 deletions

View File

@ -4,7 +4,7 @@
"name": "Linux", "name": "Linux",
"includePath": [ "includePath": [
"${workspaceFolder}/**", "${workspaceFolder}/**",
"/opt/Libraries/boost_1_85_0/include" "/opt/Libraries/boost_1_86_0/include"
], ],
"defines": [], "defines": [],
"compilerPath": "/usr/bin/gcc", "compilerPath": "/usr/bin/gcc",

View File

@ -32,6 +32,7 @@ add_executable(UnitTest main.cpp
Universal/BoostLogTest.cpp Universal/BoostLogTest.cpp
Universal/DateTimeTest.cpp Universal/DateTimeTest.cpp
Universal/MessageManagerTest.cpp Universal/MessageManagerTest.cpp
Universal/PromiseTest.cpp
Universal/SingletonTest.cpp Universal/SingletonTest.cpp
) )

View File

@ -0,0 +1,300 @@
#include "Promise.h"
#include <boost/test/unit_test.hpp>
#include <sstream>
#include <variant>
#include <vector>
template <typename T>
static inline T waitForValue(const Promise<T> &promise, const T &initial) {
T value(initial);
promise.then([&](const T &res) { value = res; }).wait();
return value;
}
template <typename T>
static inline T waitForValue(const Promise<void> &promise, const T &initial, const T &expected) {
T value(initial);
promise.then([&]() { value = expected; }).wait();
return value;
}
BOOST_AUTO_TEST_CASE(Then) {
std::vector<std::variant<int, std::string>> values;
auto input = Promise<int>::resolve(42);
auto output = input.then([&](int res) {
values.push_back(res);
return std::to_string(res + 1);
});
output.then([&](const std::string &res) { values.push_back(res); }).then([&]() { values.push_back(44); }).wait();
BOOST_CHECK_EQUAL(values.size(), 3);
BOOST_CHECK_EQUAL(std::get<int>(values[0]), 42);
BOOST_CHECK_EQUAL(std::get<std::string>(values[1]), "43");
BOOST_CHECK_EQUAL(std::get<int>(values[2]), 44);
BOOST_CHECK_EQUAL(input.isFulfilled(), true);
BOOST_CHECK_EQUAL(output.isFulfilled(), true);
// auto p= new int;
}
BOOST_AUTO_TEST_CASE(ThenResolveAsync) {
std::thread thread;
auto promise = Promise<int>::resolve(42).then([&thread](int res) {
return Promise<std::string>{[&thread, res](const PromiseResolve<std::string> &resolve) {
thread = std::thread([resolve, res]() {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::ostringstream oss;
oss << "foo" << res;
resolve(oss.str());
});
}};
});
std::string value;
promise.then([&](const std::string &res) { value = res; }).wait();
static_assert((std::is_same_v<decltype(promise), Promise<std::string>>));
BOOST_CHECK_EQUAL(value, "foo42");
BOOST_CHECK_EQUAL(promise.isFulfilled(), true);
if (thread.joinable()) {
thread.join();
}
}
BOOST_AUTO_TEST_CASE(ThenRejectSync) {
auto input = Promise<int>::resolve(42);
auto output = input.then([](int res) {
std::ostringstream oss;
oss << "foo" << res;
throw oss.str();
return 42;
});
std::string error;
output.then([&](int res) { error += "bar" + std::to_string(res); }).fail([&](const std::string &err) { error += err; }).wait();
BOOST_CHECK_EQUAL(error, "foo42");
BOOST_CHECK_EQUAL(input.isFulfilled(), true);
BOOST_CHECK_EQUAL(output.isRejected(), true);
}
BOOST_AUTO_TEST_CASE(ThenRejectAsync) {
std::thread thread;
auto p = Promise<int>::resolve(42).then([&thread](int res) {
return Promise<void>{[&thread, res](const PromiseResolve<void> &, const PromiseReject<void> &reject) {
thread = std::thread([reject, res]() {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::ostringstream oss;
oss << "foo" << res;
reject(oss.str());
});
}};
});
static_assert((std::is_same_v<decltype(p), Promise<void>>));
std::string error;
p.fail([&](const std::string &err) { error = err; }).wait();
BOOST_CHECK_EQUAL(error, "foo42");
BOOST_CHECK_EQUAL(p.isRejected(), true);
if (thread.joinable()) {
thread.join();
}
}
BOOST_AUTO_TEST_CASE(ThenSkipResult) {
auto p = Promise<int>::resolve(42);
int value = -1;
p.then([&]() { value = 43; }).wait();
static_assert((std::is_same<decltype(p), Promise<int>>::value));
BOOST_CHECK_EQUAL(value, 43);
}
BOOST_AUTO_TEST_CASE(ThenNullHandler) {
{ // resolved
auto p = Promise<int>::resolve(42).then(nullptr);
int value;
p.then([&](const int &res) { value = res; }).wait();
BOOST_CHECK_EQUAL(value, 42);
BOOST_CHECK_EQUAL(p.isFulfilled(), true);
}
{ // rejected
auto p = Promise<int>::reject(std::string{"foo"}).then(nullptr);
std::string error;
p.fail([&](const std::string &err) {
error = err;
return 0;
}).wait();
BOOST_CHECK_EQUAL(error, "foo");
BOOST_CHECK_EQUAL(p.isRejected(), true);
}
}
const float kRes = 0.42f;
const float kFail = -1.f;
float fnNoArg() {
return kRes;
}
float fnArgByVal(float v) {
return v;
}
float fnArgByRef(const float &v) {
return v;
}
class Klass {
public: // STATICS
static float kFnNoArg() {
return kRes;
}
static float kFnArgByVal(float v) {
return v;
}
static float kFnArgByRef(const float &v) {
return v;
}
public:
Klass(float v) : m_v{v} {
}
float fnNoArg() const {
return m_v;
}
float fnArgByVal(float v) const {
return v + m_v;
}
float fnArgByRef(const float &v) const {
return v + m_v;
}
private:
const float m_v;
};
BOOST_AUTO_TEST_CASE(ThenFunctionPtrHandlers) {
{ // Global functions.
auto p0 = Promise<void>::resolve().then(&fnNoArg);
auto p1 = Promise<float>::resolve(kRes).then(&fnArgByVal);
auto p2 = Promise<float>::resolve(kRes).then(&fnArgByRef);
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
}
{ // Static member functions.
auto p0 = Promise<void>::resolve().then(&Klass::kFnNoArg);
auto p1 = Promise<float>::resolve(kRes).then(&Klass::kFnArgByVal);
auto p2 = Promise<float>::resolve(kRes).then(&Klass::kFnArgByRef);
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
}
}
BOOST_AUTO_TEST_CASE(ThenStdFunctionHandlers) {
{ // lvalue.
std::function<float()> stdFnNoArg = fnNoArg;
std::function<float(float)> stdFnArgByVal = fnArgByVal;
std::function<float(const float &)> stdFnArgByRef = fnArgByRef;
auto p0 = Promise<void>::resolve().then(stdFnNoArg);
auto p1 = Promise<float>::resolve(kRes).then(stdFnArgByVal);
auto p2 = Promise<float>::resolve(kRes).then(stdFnArgByRef);
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
}
{ // const lvalue.
const std::function<float()> stdFnNoArg = fnNoArg;
const std::function<float(float)> stdFnArgByVal = fnArgByVal;
const std::function<float(const float &)> stdFnArgByRef = fnArgByRef;
auto p0 = Promise<void>::resolve().then(stdFnNoArg);
auto p1 = Promise<float>::resolve(kRes).then(stdFnArgByVal);
auto p2 = Promise<float>::resolve(kRes).then(stdFnArgByRef);
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
}
{ // rvalue.
auto p0 = Promise<void>::resolve().then(std::function<float()>{fnNoArg});
auto p1 = Promise<float>::resolve(kRes).then(std::function<float(float)>{fnArgByVal});
auto p2 = Promise<float>::resolve(kRes).then(std::function<float(const float &)>{fnArgByRef});
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
}
}
BOOST_AUTO_TEST_CASE(ThenStdBindHandlers) {
using namespace std::placeholders;
const float val{42.f};
const Klass obj{val};
const std::function<float()> bindNoArg = std::bind(&Klass::fnNoArg, &obj);
const std::function<float(float)> bindArgByVal = std::bind(&Klass::fnArgByVal, &obj, _1);
const std::function<float(const float &)> bindArgByRef = std::bind(&Klass::fnArgByRef, &obj, _1);
auto p0 = Promise<void>::resolve().then(bindNoArg);
auto p1 = Promise<float>::resolve(kRes).then(bindArgByVal);
auto p2 = Promise<float>::resolve(kRes).then(bindArgByRef);
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), val);
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), val + kRes);
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), val + kRes);
}
BOOST_AUTO_TEST_CASE(ThenLambdaHandlers) {
{ // lvalue.
auto lambdaNoArg = []() { return kRes; };
auto lambdaArgByVal = [](float v) { return v; };
auto lambdaArgByRef = [](const float &v) { return v; };
auto p0 = Promise<void>::resolve().then(lambdaNoArg);
auto p1 = Promise<float>::resolve(kRes).then(lambdaArgByVal);
auto p2 = Promise<float>::resolve(kRes).then(lambdaArgByRef);
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
}
{ // const lvalue.
const auto lambdaNoArg = []() { return kRes; };
const auto lambdaArgByVal = [](float v) { return v; };
const auto lambdaArgByRef = [](const float &v) { return v; };
auto p0 = Promise<void>::resolve().then(lambdaNoArg);
auto p1 = Promise<float>::resolve(kRes).then(lambdaArgByVal);
auto p2 = Promise<float>::resolve(kRes).then(lambdaArgByRef);
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
}
{ // rvalue.
auto p0 = Promise<void>::resolve().then([]() { return kRes; });
auto p1 = Promise<float>::resolve(kRes).then([](float v) { return v; });
auto p2 = Promise<float>::resolve(kRes).then([](const float &v) { return v; });
BOOST_CHECK_EQUAL(waitForValue(p0, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p1, kFail), kRes);
BOOST_CHECK_EQUAL(waitForValue(p2, kFail), kRes);
}
}

View File

@ -121,8 +121,87 @@ class FunctionTraits : public FunctionTraits<decltype(&Callable::operator())> {}
template <typename Function> template <typename Function>
typename FunctionTraits<std::decay_t<Function>>::StlFunctionType makeStlFunction(Function &&lambda) { typename FunctionTraits<std::decay_t<Function>>::StlFunctionType makeStlFunction(Function &&lambda) {
return static_cast<typename FunctionTraits<std::decay_t<Function>>::StlFunctionType>( return static_cast<typename FunctionTraits<std::decay_t<Function>>::StlFunctionType>(std::forward<Function>(lambda));
std::forward<Function>(lambda));
} }
template <typename T>
using Unqualified = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
/*!
* \struct HasCallOperator
* http://stackoverflow.com/a/5117641
*/
template <typename T>
struct HasCallOperator {
template <typename U>
static char check(decltype(&U::operator(), char(0)));
template <typename U>
static char (&check(...))[2];
static const bool value = (sizeof(check<T>(0)) == 1);
};
/*!
* \struct ArgsOf
* http://stackoverflow.com/a/7943765
* http://stackoverflow.com/a/27885283
*/
template <typename... Args>
struct ArgsTraits {
using types = std::tuple<Args...>;
using first = typename std::tuple_element<0, types>::type;
static const size_t count = std::tuple_size<types>::value;
};
template <>
struct ArgsTraits<> {
using types = std::tuple<>;
using first = void;
static const size_t count = 0;
};
// Fallback implementation, including types (T) which are not functions but
// also lambda with `auto` arguments, which are not covered but still valid
// callbacks (see the QPromiseBase<T> template constructor).
template <typename T, typename Enabled = void>
struct ArgsOf : public ArgsTraits<> {};
// Partial specialization for null function.
template <>
struct ArgsOf<std::nullptr_t> : public ArgsTraits<> {};
// Partial specialization for type with a non-overloaded operator().
// This applies to lambda, std::function but not to std::bind result.
template <typename T>
struct ArgsOf<T, typename std::enable_if<HasCallOperator<T>::value>::type> : public ArgsOf<decltype(&T::operator())> {};
// Partial specialization to remove reference and rvalue (e.g. lambda, std::function, etc.).
template <typename T>
struct ArgsOf<T &> : public ArgsOf<T> {};
template <typename T>
struct ArgsOf<T &&> : public ArgsOf<T> {};
// Partial specialization for function type.
template <typename R, typename... Args>
struct ArgsOf<R(Args...)> : public ArgsTraits<Args...> {};
// Partial specialization for function pointer.
template <typename R, typename... Args>
struct ArgsOf<R (*)(Args...)> : public ArgsTraits<Args...> {};
// Partial specialization for pointer-to-member-function (i.e. operator()'s).
template <typename R, typename T, typename... Args>
struct ArgsOf<R (T::*)(Args...)> : public ArgsTraits<Args...> {};
template <typename R, typename T, typename... Args>
struct ArgsOf<R (T::*)(Args...) const> : public ArgsTraits<Args...> {};
template <typename R, typename T, typename... Args>
struct ArgsOf<R (T::*)(Args...) volatile> : public ArgsTraits<Args...> {};
template <typename R, typename T, typename... Args>
struct ArgsOf<R (T::*)(Args...) const volatile> : public ArgsTraits<Args...> {};
#endif // FUNCTIONTRAITS_H #endif // FUNCTIONTRAITS_H

View File

@ -0,0 +1,183 @@
#ifndef __PROMISEDATA_H__
#define __PROMISEDATA_H__
#include <cassert>
#include <list>
#include <memory>
#include <mutex>
template <typename T>
class PromiseValue {
public:
PromiseValue() {
}
PromiseValue(const T &data) : m_data(std::make_shared<T>(data)) {
}
PromiseValue(T &&data) : m_data(std::make_shared<T>(std::forward<T>(data))) {
}
bool isNull() const {
return m_data == nullptr;
}
const T &data() const {
return *m_data;
}
private:
std::shared_ptr<T> m_data;
};
class PromiseError {
public:
template <typename T>
PromiseError(const T &value) {
try {
throw value;
} catch (...) {
m_data = std::current_exception();
}
}
PromiseError() {
}
PromiseError(const std::exception_ptr &exception) : m_data{exception} {
}
void rethrow() const {
std::rethrow_exception(m_data);
}
bool isNull() const {
return m_data == nullptr;
}
private:
std::exception_ptr m_data; // std::exception_ptr is already a shared pointer
};
template <typename T, typename F>
class PromiseDataBase {
public:
using Handler = std::function<F>;
using Catcher = std::function<void(const PromiseError &)>;
bool isFulfilled() const {
return !isPending() && m_error.isNull();
}
bool isRejected() const {
return !isPending() && !m_error.isNull();
}
bool isPending() const {
std::lock_guard locker{m_mutex};
return !m_settled;
}
void addHandler(std::function<F> handler) {
std::lock_guard locker{m_mutex};
m_handlers.push_back(std::move(handler));
}
void addCatcher(std::function<void(const PromiseError &)> catcher) {
std::lock_guard locker{m_mutex};
m_catchers.push_back(std::move(catcher));
}
template <typename E>
void reject(E &&error) {
assert(isPending());
assert(m_error.isNull());
m_error = PromiseError{std::forward<E>(error)};
setSettled();
}
const PromiseError &error() const {
assert(isRejected());
return m_error;
}
void dispatch() {
if (isPending()) {
return;
}
m_mutex.lock();
auto handlers = std::move(m_handlers);
auto catchers = std::move(m_catchers);
m_mutex.unlock();
if (m_error.isNull()) {
notify(handlers);
return;
}
PromiseError error = m_error;
assert(!error.isNull());
for (auto &catcher : catchers) {
catcher(error);
}
}
protected:
virtual void notify(const std::list<Handler> &handlers) = 0;
void setSettled() {
std::lock_guard locker{m_mutex};
assert(!m_settled);
m_settled = true;
}
mutable std::mutex m_mutex;
private:
bool m_settled = false;
std::list<Handler> m_handlers;
std::list<Catcher> m_catchers;
PromiseError m_error;
};
template <typename T>
class PromiseData : public PromiseDataBase<T, void(const T &)> {
using Handler = typename PromiseDataBase<T, void(const T &)>::Handler;
public:
template <typename V>
void resolve(V &&value) {
assert(this->isPending());
assert(m_value.isNull());
m_value = PromiseValue<T>{std::forward<V>(value)};
this->setSettled();
}
const PromiseValue<T> &value() const {
assert(this->isFulfilled());
return m_value;
}
protected:
void notify(const std::list<Handler> &handlers) final {
PromiseValue<T> value = m_value;
assert(!value.isNull());
for (auto &handler : handlers) {
handler(value.data());
}
}
private:
PromiseValue<T> m_value;
};
template <>
class PromiseData<void> : public PromiseDataBase<void, void()> {
using Handler = PromiseDataBase<void, void()>::Handler;
public:
void resolve() {
setSettled();
}
protected:
void notify(const std::list<Handler> &handlers) {
for (const auto &handler : handlers) {
handler();
}
}
};
#endif // __PROMISEDATA_H__

View File

@ -0,0 +1,10 @@
#ifndef __PROMISEEXCEPTION_H__
#define __PROMISEEXCEPTION_H__
#include <exception>
class PromiseUndefinedException : public std::exception {
public:
};
#endif // __PROMISEEXCEPTION_H__

View File

@ -0,0 +1,203 @@
#ifndef __PROMISEHANDLER_H__
#define __PROMISEHANDLER_H__
#include "../FunctionTraits.h"
#include <type_traits>
#include <exception>
#include "PromiseData.h"
template <typename T>
class Promise;
template <typename T>
struct PromiseDeduce {
using Type = Promise<T>;
};
template <typename T>
struct PromiseDeduce<T &> : public PromiseDeduce<T> {};
template <typename T>
struct PromiseDeduce<const T> : public PromiseDeduce<T> {};
template <typename T>
struct PromiseDeduce<const volatile T> : public PromiseDeduce<T> {};
template <typename T>
struct PromiseDeduce<Promise<T>> : public PromiseDeduce<T> {};
template <typename T>
struct PromiseFulfill {
template <typename V, typename TResolve, typename TReject>
static void call(V &&value, const TResolve &resolve, const TReject &) {
resolve(std::forward<V>(value));
}
};
template <typename T>
struct PromiseFulfill<Promise<T>> {
template <typename TResolve, typename TReject>
static void call(const Promise<T> &promise, const TResolve &resolve, const TReject &reject) {
if (promise.isFulfilled()) {
resolve(promise.m_d->value());
} else if (promise.isRejected()) {
reject(promise.m_d->error());
} else {
promise.then([=]() { resolve(promise.m_d->value()); },
[=]() { // catch all
reject(promise.m_d->error());
});
}
}
};
template <>
struct PromiseFulfill<Promise<void>> {
template <typename TPromise, typename TResolve, typename TReject>
static void call(const TPromise &promise, const TResolve &resolve, const TReject &reject) {
if (promise.isFulfilled()) {
resolve();
} else if (promise.isRejected()) {
reject(promise.m_d->error());
} else {
promise.then([=]() { resolve(); },
[=]() { // catch all
reject(promise.m_d->error());
});
}
}
};
template <typename Result>
struct PromiseDispatch {
template <typename Resolve, typename Reject, typename Functor, typename... Args>
static void call(const Resolve &resolve, const Reject &reject, Functor fn, Args &&...args) {
try {
PromiseFulfill<std::decay_t<Result>>::call(fn(std::forward<Args>(args)...), resolve, reject);
} catch (...) {
reject(std::current_exception());
}
}
};
template <>
struct PromiseDispatch<void> {
template <typename Resolve, typename Reject, typename Functor, typename... Args>
static void call(const Resolve &resolve, const Reject &reject, Functor fn, Args &&...args) {
try {
fn(std::forward<Args>(args)...);
resolve();
} catch (...) {
reject(std::current_exception());
}
}
};
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
struct PromiseHandler {
using ResType = std::invoke_result_t<THandler, T>;
using PromiseType = typename PromiseDeduce<ResType>::Type;
template <typename TResolve, typename TReject>
static std::function<void(const T &)> create(const THandler &handler, const TResolve &resolve, const TReject &reject) {
return [=](const T &value) { PromiseDispatch<ResType>::call(resolve, reject, handler, value); };
}
};
template <typename T, typename THandler>
struct PromiseHandler<T, THandler, void> {
using ResType = std::invoke_result_t<THandler>;
using PromiseType = typename PromiseDeduce<ResType>::Type;
template <typename TResolve, typename TReject>
static std::function<void(const T &)> create(const THandler &handler, const TResolve &resolve, const TReject &reject) {
return [=](const T &) { PromiseDispatch<ResType>::call(resolve, reject, handler); };
}
};
template <typename THandler>
struct PromiseHandler<void, THandler, void> {
using ResType = std::invoke_result_t<THandler>;
using PromiseType = typename PromiseDeduce<ResType>::Type;
template <typename TResolve, typename TReject>
static std::function<void()> create(const THandler &handler, const TResolve &resolve, const TReject &reject) {
return [=]() { PromiseDispatch<ResType>::call(resolve, reject, handler); };
}
};
template <typename T>
struct PromiseHandler<T, std::nullptr_t, void> {
using PromiseType = Promise<T>;
template <typename TResolve, typename TReject>
static std::function<void(const T &)> create(std::nullptr_t, const TResolve &resolve, const TReject &reject) {
return [=](const T &value) {
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
// promise2 must be fulfilled with the same value as promise1.
PromiseFulfill<T>::call(std::move(T(value)), resolve, reject);
};
}
};
template <>
struct PromiseHandler<void, std::nullptr_t, void> {
using PromiseType = Promise<void>;
template <typename TResolve, typename TReject>
static std::function<void()> create(std::nullptr_t, const TResolve &resolve, const TReject &) {
return [=]() {
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
// promise2 must be fulfilled with the same value as promise1.
resolve();
};
}
};
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
struct PromiseCatcher {
using ResType = std::invoke_result_t<THandler, TArg>;
template <typename TResolve, typename TReject>
static std::function<void(const PromiseError &)> create(const THandler &handler, const TResolve &resolve, const TReject &reject) {
return [=](const PromiseError &error) {
try {
error.rethrow();
} catch (const TArg &argError) {
PromiseDispatch<ResType>::call(resolve, reject, handler, argError);
} catch (...) {
reject(std::current_exception());
}
};
}
};
template <typename T, typename THandler>
struct PromiseCatcher<T, THandler, void> {
using ResType = typename std::invoke_result_t<THandler>;
template <typename TResolve, typename TReject>
static std::function<void(const PromiseError &)> create(const THandler &handler, const TResolve &resolve, const TReject &reject) {
return [=](const PromiseError &error) {
try {
error.rethrow();
} catch (...) {
PromiseDispatch<ResType>::call(resolve, reject, handler);
}
};
}
};
template <typename T>
struct PromiseCatcher<T, std::nullptr_t, void> {
template <typename TResolve, typename TReject>
static std::function<void(const PromiseError &)> create(std::nullptr_t, const TResolve &, const TReject &reject) {
return [=](const PromiseError &error) {
// 2.2.7.4. If onRejected is not a function and promise1 is rejected,
// promise2 must be rejected with the same reason as promise1
reject(error);
};
}
};
#endif // __PROMISEHANDLER_H__

View File

@ -0,0 +1,91 @@
#ifndef __PROMISERESOLVER_H__
#define __PROMISERESOLVER_H__
#include "PromiseException.h"
template <typename T>
class Promise;
template <typename T>
class PromiseResolver {
public:
PromiseResolver(Promise<T> promise) : m_d{promise.m_d} {
}
template <typename V>
void resolve(V &&value) {
if (m_d) {
assert(m_d->isPending());
m_d->resolve(std::forward<V>(value));
m_d->dispatch();
}
}
void resolve() {
if (m_d) {
assert(m_d->isPending());
m_d->resolve();
m_d->dispatch();
}
}
template <typename E>
void reject(E &&error) {
if (m_d) {
assert(m_d->isPending());
m_d->reject(std::forward<E>(error));
m_d->dispatch();
}
}
void reject() {
if (m_d) {
assert(m_d->isPending());
m_d->reject(PromiseUndefinedException{});
m_d->dispatch();
}
}
private:
std::shared_ptr<PromiseData<T>> m_d;
};
template <typename T>
class PromiseResolve {
public:
PromiseResolve(PromiseResolver<T> resolver) : m_resolver{std::move(resolver)} {
}
template <typename V>
void operator()(V &&value) const {
m_resolver.resolve(std::forward<V>(value));
}
void operator()() const {
m_resolver.resolve();
}
private:
mutable PromiseResolver<T> m_resolver;
};
template <typename T>
class PromiseReject {
public:
PromiseReject(PromiseResolver<T> resolver) : m_resolver{std::move(resolver)} {
}
template <typename E>
void operator()(E &&error) const {
m_resolver.reject(std::forward<E>(error));
}
void operator()() const {
m_resolver.reject();
}
private:
mutable PromiseResolver<T> m_resolver;
};
#endif // __PROMISERESOLVER_H__

134
Universal/Promise.h Normal file
View File

@ -0,0 +1,134 @@
#ifndef __PROMISE_H__
#define __PROMISE_H__
#include "Private/PromiseHandler.h"
#include "Private/PromiseResolver.h"
#include <thread>
template <typename T>
class Promise;
template <typename T>
class PromiseBase {
friend struct PromiseFulfill<Promise<T>>;
public:
using Type = T;
template <typename F, typename std::enable_if<ArgsOf<F>::count == 1, int>::type = 0>
PromiseBase(F callback) : m_d{std::make_shared<PromiseData<T>>()} {
PromiseResolver<T> resolver{*this};
try {
callback(PromiseResolve<T>(resolver));
} catch (...) {
resolver.reject(std::current_exception());
}
}
template <typename F, typename std::enable_if<ArgsOf<F>::count != 1, int>::type = 0>
PromiseBase(F callback) : m_d{std::make_shared<PromiseData<T>>()} {
PromiseResolver<T> resolver{*this};
try {
callback(PromiseResolve<T>(resolver), PromiseReject<T>(resolver));
} catch (...) {
resolver.reject(std::current_exception());
}
}
PromiseBase(const PromiseBase<T> &other) : m_d{other.m_d} {
}
PromiseBase(const Promise<T> &other) : m_d{other.m_d} {
}
PromiseBase(PromiseBase<T> &&other) {
swap(other);
}
void swap(PromiseBase<T> &other) {
std::swap(m_d, other.m_d);
}
bool isFulfilled() const {
return m_d->isFulfilled();
}
bool isRejected() const {
return m_d->isRejected();
}
bool isPending() const {
return m_d->isPending();
}
template <typename TFulfilled, typename TRejected>
typename PromiseHandler<T, TFulfilled>::PromiseType then(const TFulfilled &fulfilled, const TRejected &rejected) const {
using PromiseType = typename PromiseHandler<T, TFulfilled>::PromiseType;
PromiseType next(
[&](const PromiseResolve<typename PromiseType::Type> &resolve, const PromiseReject<typename PromiseType::Type> &reject) {
m_d->addHandler(PromiseHandler<T, TFulfilled>::create(fulfilled, resolve, reject));
m_d->addCatcher(PromiseCatcher<T, TRejected>::create(rejected, resolve, reject));
});
if (!m_d->isPending()) {
m_d->dispatch();
}
return next;
}
template <typename TFulfilled>
typename PromiseHandler<T, TFulfilled>::PromiseType then(TFulfilled &&fulfilled) const {
return then(std::forward<TFulfilled>(fulfilled), nullptr);
}
template <typename TRejected>
typename PromiseHandler<T, std::nullptr_t>::PromiseType fail(TRejected &&rejected) const {
return then(nullptr, std::forward<TRejected>(rejected));
}
Promise<T> wait() const {
while (m_d->isPending()) {
std::this_thread::yield();
}
return *this;
}
template <typename E>
static Promise<T> reject(E &&error) {
return Promise<T>{[&](const PromiseResolve<T> &, const PromiseReject<T> &reject) { reject(std::forward<E>(error)); }};
}
protected:
std::shared_ptr<PromiseData<T>> m_d;
};
template <typename T>
class Promise : public PromiseBase<T> {
friend class PromiseResolver<T>;
public:
template <typename F>
Promise(F &&resolver) : PromiseBase<T>(std::forward<F>(resolver)) {
}
static Promise<T> resolve(const T &value) {
return Promise<T>{[&](const PromiseResolve<T> &resolve) { resolve(value); }};
}
};
template <>
class Promise<void> : public PromiseBase<void> {
friend class PromiseResolver<void>;
public:
template <typename F>
Promise(F &&resolver) : PromiseBase<void>(std::forward<F>(resolver)) {
}
public:
static Promise<void> resolve() {
return Promise<void>{[](const PromiseResolve<void> &resolve) { resolve(); }};
}
private:
friend class PromiseBase<void>;
};
#endif // __PROMISE_H__

View File

@ -48,7 +48,7 @@ function cmake_scan() {
-B ${build_path} \ -B ${build_path} \
-DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_BUILD_TYPE=Debug \
-DUNIT_TEST=ON \ -DUNIT_TEST=ON \
-DBOOST_ROOT=${libraries_root}/boost_1_85_0 \ -DBOOST_ROOT=${libraries_root}/boost_1_86_0 \
-DZeroMQ_ROOT=${libraries_root}/zeromq-4.3.4_debug \ -DZeroMQ_ROOT=${libraries_root}/zeromq-4.3.4_debug \
${cmake_qt_parameters} ${cmake_qt_parameters}
} }