add promise code.
This commit is contained in:
parent
fe89ecd0f8
commit
c82f079bcb
2
.vscode/c_cpp_properties.json
vendored
2
.vscode/c_cpp_properties.json
vendored
@ -4,7 +4,7 @@
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"/opt/Libraries/boost_1_85_0/include"
|
||||
"/opt/Libraries/boost_1_86_0/include"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
|
@ -32,6 +32,7 @@ add_executable(UnitTest main.cpp
|
||||
Universal/BoostLogTest.cpp
|
||||
Universal/DateTimeTest.cpp
|
||||
Universal/MessageManagerTest.cpp
|
||||
Universal/PromiseTest.cpp
|
||||
Universal/SingletonTest.cpp
|
||||
)
|
||||
|
||||
|
300
UnitTest/Universal/PromiseTest.cpp
Normal file
300
UnitTest/Universal/PromiseTest.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -121,8 +121,87 @@ class FunctionTraits : public FunctionTraits<decltype(&Callable::operator())> {}
|
||||
|
||||
template <typename Function>
|
||||
typename FunctionTraits<std::decay_t<Function>>::StlFunctionType makeStlFunction(Function &&lambda) {
|
||||
return static_cast<typename FunctionTraits<std::decay_t<Function>>::StlFunctionType>(
|
||||
std::forward<Function>(lambda));
|
||||
return static_cast<typename FunctionTraits<std::decay_t<Function>>::StlFunctionType>(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
|
183
Universal/Private/PromiseData.h
Normal file
183
Universal/Private/PromiseData.h
Normal 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__
|
10
Universal/Private/PromiseException.h
Normal file
10
Universal/Private/PromiseException.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef __PROMISEEXCEPTION_H__
|
||||
#define __PROMISEEXCEPTION_H__
|
||||
|
||||
#include <exception>
|
||||
|
||||
class PromiseUndefinedException : public std::exception {
|
||||
public:
|
||||
};
|
||||
|
||||
#endif // __PROMISEEXCEPTION_H__
|
203
Universal/Private/PromiseHandler.h
Normal file
203
Universal/Private/PromiseHandler.h
Normal 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__
|
91
Universal/Private/PromiseResolver.h
Normal file
91
Universal/Private/PromiseResolver.h
Normal 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
134
Universal/Promise.h
Normal 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__
|
@ -48,7 +48,7 @@ function cmake_scan() {
|
||||
-B ${build_path} \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-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 \
|
||||
${cmake_qt_parameters}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user