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",
|
"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",
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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>
|
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
|
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} \
|
-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}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user