diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index abaea3c..04595fe 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -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", diff --git a/UnitTest/CMakeLists.txt b/UnitTest/CMakeLists.txt index 5402167..83cf9f3 100644 --- a/UnitTest/CMakeLists.txt +++ b/UnitTest/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable(UnitTest main.cpp Universal/BoostLogTest.cpp Universal/DateTimeTest.cpp Universal/MessageManagerTest.cpp + Universal/PromiseTest.cpp Universal/SingletonTest.cpp ) diff --git a/UnitTest/Universal/PromiseTest.cpp b/UnitTest/Universal/PromiseTest.cpp new file mode 100644 index 0000000..803e35c --- /dev/null +++ b/UnitTest/Universal/PromiseTest.cpp @@ -0,0 +1,300 @@ +#include "Promise.h" +#include +#include +#include +#include + +template +static inline T waitForValue(const Promise &promise, const T &initial) { + T value(initial); + promise.then([&](const T &res) { value = res; }).wait(); + return value; +} + +template +static inline T waitForValue(const Promise &promise, const T &initial, const T &expected) { + T value(initial); + promise.then([&]() { value = expected; }).wait(); + return value; +} + +BOOST_AUTO_TEST_CASE(Then) { + std::vector> values; + auto input = Promise::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(values[0]), 42); + BOOST_CHECK_EQUAL(std::get(values[1]), "43"); + BOOST_CHECK_EQUAL(std::get(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::resolve(42).then([&thread](int res) { + return Promise{[&thread, res](const PromiseResolve &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>)); + BOOST_CHECK_EQUAL(value, "foo42"); + BOOST_CHECK_EQUAL(promise.isFulfilled(), true); + + if (thread.joinable()) { + thread.join(); + } +} + +BOOST_AUTO_TEST_CASE(ThenRejectSync) { + auto input = Promise::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::resolve(42).then([&thread](int res) { + return Promise{[&thread, res](const PromiseResolve &, const PromiseReject &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>)); + + 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::resolve(42); + + int value = -1; + p.then([&]() { value = 43; }).wait(); + + static_assert((std::is_same>::value)); + BOOST_CHECK_EQUAL(value, 43); +} + +BOOST_AUTO_TEST_CASE(ThenNullHandler) { + { // resolved + auto p = Promise::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::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::resolve().then(&fnNoArg); + auto p1 = Promise::resolve(kRes).then(&fnArgByVal); + auto p2 = Promise::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::resolve().then(&Klass::kFnNoArg); + auto p1 = Promise::resolve(kRes).then(&Klass::kFnArgByVal); + auto p2 = Promise::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 stdFnNoArg = fnNoArg; + std::function stdFnArgByVal = fnArgByVal; + std::function stdFnArgByRef = fnArgByRef; + + auto p0 = Promise::resolve().then(stdFnNoArg); + auto p1 = Promise::resolve(kRes).then(stdFnArgByVal); + auto p2 = Promise::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 stdFnNoArg = fnNoArg; + const std::function stdFnArgByVal = fnArgByVal; + const std::function stdFnArgByRef = fnArgByRef; + + auto p0 = Promise::resolve().then(stdFnNoArg); + auto p1 = Promise::resolve(kRes).then(stdFnArgByVal); + auto p2 = Promise::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::resolve().then(std::function{fnNoArg}); + auto p1 = Promise::resolve(kRes).then(std::function{fnArgByVal}); + auto p2 = Promise::resolve(kRes).then(std::function{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 bindNoArg = std::bind(&Klass::fnNoArg, &obj); + const std::function bindArgByVal = std::bind(&Klass::fnArgByVal, &obj, _1); + const std::function bindArgByRef = std::bind(&Klass::fnArgByRef, &obj, _1); + + auto p0 = Promise::resolve().then(bindNoArg); + auto p1 = Promise::resolve(kRes).then(bindArgByVal); + auto p2 = Promise::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::resolve().then(lambdaNoArg); + auto p1 = Promise::resolve(kRes).then(lambdaArgByVal); + auto p2 = Promise::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::resolve().then(lambdaNoArg); + auto p1 = Promise::resolve(kRes).then(lambdaArgByVal); + auto p2 = Promise::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::resolve().then([]() { return kRes; }); + auto p1 = Promise::resolve(kRes).then([](float v) { return v; }); + auto p2 = Promise::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); + } +} \ No newline at end of file diff --git a/Universal/FunctionTraits.h b/Universal/FunctionTraits.h index f425d7b..8673075 100644 --- a/Universal/FunctionTraits.h +++ b/Universal/FunctionTraits.h @@ -121,8 +121,87 @@ class FunctionTraits : public FunctionTraits {} template typename FunctionTraits>::StlFunctionType makeStlFunction(Function &&lambda) { - return static_cast>::StlFunctionType>( - std::forward(lambda)); + return static_cast>::StlFunctionType>(std::forward(lambda)); } +template +using Unqualified = typename std::remove_cv::type>::type; + +/*! + * \struct HasCallOperator + * http://stackoverflow.com/a/5117641 + */ +template +struct HasCallOperator { + template + static char check(decltype(&U::operator(), char(0))); + + template + static char (&check(...))[2]; + + static const bool value = (sizeof(check(0)) == 1); +}; + +/*! + * \struct ArgsOf + * http://stackoverflow.com/a/7943765 + * http://stackoverflow.com/a/27885283 + */ +template +struct ArgsTraits { + using types = std::tuple; + using first = typename std::tuple_element<0, types>::type; + static const size_t count = std::tuple_size::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 template constructor). +template +struct ArgsOf : public ArgsTraits<> {}; + +// Partial specialization for null function. +template <> +struct ArgsOf : public ArgsTraits<> {}; + +// Partial specialization for type with a non-overloaded operator(). +// This applies to lambda, std::function but not to std::bind result. +template +struct ArgsOf::value>::type> : public ArgsOf {}; + +// Partial specialization to remove reference and rvalue (e.g. lambda, std::function, etc.). +template +struct ArgsOf : public ArgsOf {}; + +template +struct ArgsOf : public ArgsOf {}; + +// Partial specialization for function type. +template +struct ArgsOf : public ArgsTraits {}; + +// Partial specialization for function pointer. +template +struct ArgsOf : public ArgsTraits {}; + +// Partial specialization for pointer-to-member-function (i.e. operator()'s). +template +struct ArgsOf : public ArgsTraits {}; + +template +struct ArgsOf : public ArgsTraits {}; + +template +struct ArgsOf : public ArgsTraits {}; + +template +struct ArgsOf : public ArgsTraits {}; + #endif // FUNCTIONTRAITS_H \ No newline at end of file diff --git a/Universal/Private/PromiseData.h b/Universal/Private/PromiseData.h new file mode 100644 index 0000000..28f2271 --- /dev/null +++ b/Universal/Private/PromiseData.h @@ -0,0 +1,183 @@ +#ifndef __PROMISEDATA_H__ +#define __PROMISEDATA_H__ + +#include +#include +#include +#include + +template +class PromiseValue { +public: + PromiseValue() { + } + PromiseValue(const T &data) : m_data(std::make_shared(data)) { + } + PromiseValue(T &&data) : m_data(std::make_shared(std::forward(data))) { + } + bool isNull() const { + return m_data == nullptr; + } + const T &data() const { + return *m_data; + } + +private: + std::shared_ptr m_data; +}; + +class PromiseError { +public: + template + 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 +class PromiseDataBase { +public: + using Handler = std::function; + using Catcher = std::function; + + 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 handler) { + std::lock_guard locker{m_mutex}; + m_handlers.push_back(std::move(handler)); + } + + void addCatcher(std::function catcher) { + std::lock_guard locker{m_mutex}; + m_catchers.push_back(std::move(catcher)); + } + + template + void reject(E &&error) { + assert(isPending()); + assert(m_error.isNull()); + m_error = PromiseError{std::forward(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 &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 m_handlers; + std::list m_catchers; + PromiseError m_error; +}; + +template +class PromiseData : public PromiseDataBase { + using Handler = typename PromiseDataBase::Handler; + +public: + template + void resolve(V &&value) { + assert(this->isPending()); + assert(m_value.isNull()); + m_value = PromiseValue{std::forward(value)}; + this->setSettled(); + } + + const PromiseValue &value() const { + assert(this->isFulfilled()); + return m_value; + } + +protected: + void notify(const std::list &handlers) final { + PromiseValue value = m_value; + assert(!value.isNull()); + for (auto &handler : handlers) { + handler(value.data()); + } + } + +private: + PromiseValue m_value; +}; + +template <> +class PromiseData : public PromiseDataBase { + using Handler = PromiseDataBase::Handler; + +public: + void resolve() { + setSettled(); + } + +protected: + void notify(const std::list &handlers) { + for (const auto &handler : handlers) { + handler(); + } + } +}; + +#endif // __PROMISEDATA_H__ \ No newline at end of file diff --git a/Universal/Private/PromiseException.h b/Universal/Private/PromiseException.h new file mode 100644 index 0000000..a3b5b1b --- /dev/null +++ b/Universal/Private/PromiseException.h @@ -0,0 +1,10 @@ +#ifndef __PROMISEEXCEPTION_H__ +#define __PROMISEEXCEPTION_H__ + +#include + +class PromiseUndefinedException : public std::exception { +public: +}; + +#endif // __PROMISEEXCEPTION_H__ \ No newline at end of file diff --git a/Universal/Private/PromiseHandler.h b/Universal/Private/PromiseHandler.h new file mode 100644 index 0000000..5504592 --- /dev/null +++ b/Universal/Private/PromiseHandler.h @@ -0,0 +1,203 @@ +#ifndef __PROMISEHANDLER_H__ +#define __PROMISEHANDLER_H__ + +#include "../FunctionTraits.h" +#include +#include +#include "PromiseData.h" + +template +class Promise; + +template +struct PromiseDeduce { + using Type = Promise; +}; + +template +struct PromiseDeduce : public PromiseDeduce {}; + +template +struct PromiseDeduce : public PromiseDeduce {}; + +template +struct PromiseDeduce : public PromiseDeduce {}; + +template +struct PromiseDeduce> : public PromiseDeduce {}; + +template +struct PromiseFulfill { + template + static void call(V &&value, const TResolve &resolve, const TReject &) { + resolve(std::forward(value)); + } +}; + +template +struct PromiseFulfill> { + template + static void call(const Promise &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> { + template + 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 +struct PromiseDispatch { + template + static void call(const Resolve &resolve, const Reject &reject, Functor fn, Args &&...args) { + try { + PromiseFulfill>::call(fn(std::forward(args)...), resolve, reject); + } catch (...) { + reject(std::current_exception()); + } + } +}; + +template <> +struct PromiseDispatch { + template + static void call(const Resolve &resolve, const Reject &reject, Functor fn, Args &&...args) { + try { + fn(std::forward(args)...); + resolve(); + } catch (...) { + reject(std::current_exception()); + } + } +}; + +template ::first> +struct PromiseHandler { + using ResType = std::invoke_result_t; + using PromiseType = typename PromiseDeduce::Type; + + template + static std::function create(const THandler &handler, const TResolve &resolve, const TReject &reject) { + return [=](const T &value) { PromiseDispatch::call(resolve, reject, handler, value); }; + } +}; + +template +struct PromiseHandler { + using ResType = std::invoke_result_t; + using PromiseType = typename PromiseDeduce::Type; + + template + static std::function create(const THandler &handler, const TResolve &resolve, const TReject &reject) { + return [=](const T &) { PromiseDispatch::call(resolve, reject, handler); }; + } +}; + +template +struct PromiseHandler { + using ResType = std::invoke_result_t; + using PromiseType = typename PromiseDeduce::Type; + + template + static std::function create(const THandler &handler, const TResolve &resolve, const TReject &reject) { + return [=]() { PromiseDispatch::call(resolve, reject, handler); }; + } +}; + +template +struct PromiseHandler { + using PromiseType = Promise; + + template + static std::function 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::call(std::move(T(value)), resolve, reject); + }; + } +}; + +template <> +struct PromiseHandler { + using PromiseType = Promise; + + template + static std::function 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 ::first> +struct PromiseCatcher { + using ResType = std::invoke_result_t; + + template + static std::function create(const THandler &handler, const TResolve &resolve, const TReject &reject) { + return [=](const PromiseError &error) { + try { + error.rethrow(); + } catch (const TArg &argError) { + PromiseDispatch::call(resolve, reject, handler, argError); + } catch (...) { + reject(std::current_exception()); + } + }; + } +}; + +template +struct PromiseCatcher { + using ResType = typename std::invoke_result_t; + + template + static std::function create(const THandler &handler, const TResolve &resolve, const TReject &reject) { + return [=](const PromiseError &error) { + try { + error.rethrow(); + } catch (...) { + PromiseDispatch::call(resolve, reject, handler); + } + }; + } +}; + +template +struct PromiseCatcher { + template + static std::function 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__ \ No newline at end of file diff --git a/Universal/Private/PromiseResolver.h b/Universal/Private/PromiseResolver.h new file mode 100644 index 0000000..b17d489 --- /dev/null +++ b/Universal/Private/PromiseResolver.h @@ -0,0 +1,91 @@ +#ifndef __PROMISERESOLVER_H__ +#define __PROMISERESOLVER_H__ + +#include "PromiseException.h" + +template +class Promise; + +template +class PromiseResolver { +public: + PromiseResolver(Promise promise) : m_d{promise.m_d} { + } + + template + void resolve(V &&value) { + if (m_d) { + assert(m_d->isPending()); + m_d->resolve(std::forward(value)); + m_d->dispatch(); + } + } + + void resolve() { + if (m_d) { + assert(m_d->isPending()); + m_d->resolve(); + m_d->dispatch(); + } + } + + template + void reject(E &&error) { + if (m_d) { + assert(m_d->isPending()); + m_d->reject(std::forward(error)); + m_d->dispatch(); + } + } + + void reject() { + if (m_d) { + assert(m_d->isPending()); + m_d->reject(PromiseUndefinedException{}); + m_d->dispatch(); + } + } + +private: + std::shared_ptr> m_d; +}; + +template +class PromiseResolve { +public: + PromiseResolve(PromiseResolver resolver) : m_resolver{std::move(resolver)} { + } + + template + void operator()(V &&value) const { + m_resolver.resolve(std::forward(value)); + } + + void operator()() const { + m_resolver.resolve(); + } + +private: + mutable PromiseResolver m_resolver; +}; + +template +class PromiseReject { +public: + PromiseReject(PromiseResolver resolver) : m_resolver{std::move(resolver)} { + } + + template + void operator()(E &&error) const { + m_resolver.reject(std::forward(error)); + } + + void operator()() const { + m_resolver.reject(); + } + +private: + mutable PromiseResolver m_resolver; +}; + +#endif // __PROMISERESOLVER_H__ \ No newline at end of file diff --git a/Universal/Promise.h b/Universal/Promise.h new file mode 100644 index 0000000..eba3f55 --- /dev/null +++ b/Universal/Promise.h @@ -0,0 +1,134 @@ +#ifndef __PROMISE_H__ +#define __PROMISE_H__ + +#include "Private/PromiseHandler.h" +#include "Private/PromiseResolver.h" +#include + +template +class Promise; + +template +class PromiseBase { + friend struct PromiseFulfill>; + +public: + using Type = T; + template ::count == 1, int>::type = 0> + PromiseBase(F callback) : m_d{std::make_shared>()} { + PromiseResolver resolver{*this}; + try { + callback(PromiseResolve(resolver)); + } catch (...) { + resolver.reject(std::current_exception()); + } + } + + template ::count != 1, int>::type = 0> + PromiseBase(F callback) : m_d{std::make_shared>()} { + PromiseResolver resolver{*this}; + try { + callback(PromiseResolve(resolver), PromiseReject(resolver)); + } catch (...) { + resolver.reject(std::current_exception()); + } + } + + PromiseBase(const PromiseBase &other) : m_d{other.m_d} { + } + PromiseBase(const Promise &other) : m_d{other.m_d} { + } + PromiseBase(PromiseBase &&other) { + swap(other); + } + + void swap(PromiseBase &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 PromiseHandler::PromiseType then(const TFulfilled &fulfilled, const TRejected &rejected) const { + using PromiseType = typename PromiseHandler::PromiseType; + + PromiseType next( + [&](const PromiseResolve &resolve, const PromiseReject &reject) { + m_d->addHandler(PromiseHandler::create(fulfilled, resolve, reject)); + m_d->addCatcher(PromiseCatcher::create(rejected, resolve, reject)); + }); + + if (!m_d->isPending()) { + m_d->dispatch(); + } + + return next; + } + + template + typename PromiseHandler::PromiseType then(TFulfilled &&fulfilled) const { + return then(std::forward(fulfilled), nullptr); + } + + template + typename PromiseHandler::PromiseType fail(TRejected &&rejected) const { + return then(nullptr, std::forward(rejected)); + } + + Promise wait() const { + while (m_d->isPending()) { + std::this_thread::yield(); + } + return *this; + } + + template + static Promise reject(E &&error) { + return Promise{[&](const PromiseResolve &, const PromiseReject &reject) { reject(std::forward(error)); }}; + } + +protected: + std::shared_ptr> m_d; +}; + +template +class Promise : public PromiseBase { + friend class PromiseResolver; + +public: + template + Promise(F &&resolver) : PromiseBase(std::forward(resolver)) { + } + + static Promise resolve(const T &value) { + return Promise{[&](const PromiseResolve &resolve) { resolve(value); }}; + } +}; + +template <> +class Promise : public PromiseBase { + friend class PromiseResolver; + +public: + template + Promise(F &&resolver) : PromiseBase(std::forward(resolver)) { + } + +public: + static Promise resolve() { + return Promise{[](const PromiseResolve &resolve) { resolve(); }}; + } + +private: + friend class PromiseBase; +}; + +#endif // __PROMISE_H__ \ No newline at end of file diff --git a/resource/deploy.sh b/resource/deploy.sh index d4785f7..5377215 100755 --- a/resource/deploy.sh +++ b/resource/deploy.sh @@ -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} }