From 4900f7a7533ca0d70db7dd5cb40d3fc8c5996a04 Mon Sep 17 00:00:00 2001 From: Determinant Date: Wed, 31 Jan 2018 17:45:09 -0500 Subject: update to a better interface and reduce the use of type erasures --- promise.hpp | 231 ++++++++++++++++++++++++++++++++++++------------------------ test.cpp | 38 +++++----- 2 files changed, 158 insertions(+), 111 deletions(-) diff --git a/promise.hpp b/promise.hpp index 397ef70..b9f5cfc 100644 --- a/promise.hpp +++ b/promise.hpp @@ -6,6 +6,17 @@ #include #include +template +struct function_traits: + public function_traits {}; + +template +struct function_traits +{ + using ret_type = ReturnType; + using arg_type = ArgType; +}; + /** Implement type-safe Promise primitives similar to the ones specified by * Javascript A+. */ namespace promise { @@ -20,7 +31,9 @@ namespace promise { class promise_t { std::shared_ptr ptr; public: - promise_t(function callback) { + + template + promise_t(Func callback) { ptr = std::make_shared(); callback(*this); } @@ -28,24 +41,25 @@ namespace promise { template inline void resolve(T result) const; template inline void reject(T reason) const; - template - inline promise_t then(function fulfilled_callback, - function rejected_callback) const; + template + inline promise_t then_any(FuncFulfilled fulfilled_callback, + FuncRejected rejected_callback) const; - template - inline promise_t then(function fulfilled_callback) const; + template + inline promise_t then_any(FuncFulfilled fulfilled_callback) const; - template - inline promise_t fail(function rejected_callback) const; + template + inline promise_t fail_any(FuncRejected rejected_callback) const; - template - inline promise_t then(function on_fulfilled) const; + template + inline promise_t then(FuncFulfilled on_fulfilled) const; - template - inline promise_t then(function on_fulfilled, function on_rejected) const; + template + inline promise_t then(FuncFulfilled on_fulfilled, + FuncRejected on_rejected) const; - template - inline promise_t fail(function on_fulfilled) const; + template + inline promise_t fail(FuncRejected on_rejected) const; }; #define PROMISE_ERR_INVALID_STATE do {throw std::runtime_error("invalid promise state");} while (0) @@ -93,50 +107,58 @@ namespace promise { rejected_callbacks.push_back(cb); } - function gen_on_fulfilled( - function on_fulfilled, - const promise_t &npm) { - return [this, on_fulfilled, npm]() { - on_fulfilled(result).then( - function([npm] (pm_any_t result_) { + template + typename std::enable_if< + std::is_same::ret_type, + promise_t>::value>::type + gen_on_fulfilled(Func on_fulfilled, const promise_t &npm, function &ret) { + ret = [this, on_fulfilled, npm]() { + on_fulfilled(result).then_any( + [npm] (pm_any_t result_) { npm.resolve(result_); - return none; - }), - function([npm] (pm_any_t reason_) { + return pm_any_t(none); + }, + [npm] (pm_any_t reason_) { npm.reject(reason_); - return none; - })); + return pm_any_t(none); + }); }; } - function gen_on_fulfilled( - function on_fulfilled, - const promise_t &npm) { - return [this, on_fulfilled, npm]() { + template + typename std::enable_if< + std::is_same::ret_type, + pm_any_t>::value>::type + gen_on_fulfilled(Func on_fulfilled, const promise_t &npm, function &ret) { + ret = [this, on_fulfilled, npm]() { npm.resolve(on_fulfilled(result)); }; } - function gen_on_rejected( - function on_rejected, - const promise_t &npm) { - return [this, on_rejected, npm]() { - on_rejected(reason).then( - function([npm] (pm_any_t result_) { + template + typename std::enable_if< + std::is_same::ret_type, + promise_t>::value>::type + gen_on_rejected(Func on_rejected, const promise_t &npm, function &ret) { + ret = [this, on_rejected, npm]() { + on_rejected(reason).then_any( + [npm] (pm_any_t result_) { npm.resolve(result_); return none; - }), - function([npm] (pm_any_t reason_) { + }, + [npm] (pm_any_t reason_) { npm.reject(reason_); return none; - })); + }); }; } - function gen_on_rejected( - function on_rejected, - const promise_t &npm) { - return [this, on_rejected, npm]() { + template + typename std::enable_if< + std::is_same::ret_type, + pm_any_t>::value>::type + gen_on_rejected(Func on_rejected, const promise_t &npm, function &ret) { + ret = [this, on_rejected, npm]() { npm.reject(on_rejected(reason)); }; } @@ -156,40 +178,51 @@ namespace promise { //printf("%lx freed\n", (uintptr_t)this); } - template - promise_t then(function on_fulfilled, - function on_rejected) { + template + promise_t then(FuncFulfilled on_fulfilled, + FuncRejected on_rejected) { switch (state) { case State::Pending: return promise_t([this, on_fulfilled, on_rejected](promise_t npm) { - add_on_fulfilled(gen_on_fulfilled(on_fulfilled, npm)); - add_on_rejected(gen_on_rejected(on_rejected, npm)); + function ret; + gen_on_fulfilled(on_fulfilled, npm, ret); + add_on_fulfilled(ret); + gen_on_rejected(on_rejected, npm, ret); + add_on_rejected(ret); }); case State::Fulfilled: return promise_t([this, on_fulfilled](promise_t npm) { - gen_on_fulfilled(on_fulfilled, npm)(); + function ret; + gen_on_fulfilled(on_fulfilled, npm, ret); + ret(); }); case State::Rejected: return promise_t([this, on_rejected](promise_t npm) { - gen_on_rejected(on_rejected, npm)(); + function ret; + gen_on_rejected(on_rejected, npm, ret); + ret(); }); default: PROMISE_ERR_INVALID_STATE; } } - template - promise_t then(function on_fulfilled) { + template + promise_t then(FuncFulfilled on_fulfilled) { switch (state) { case State::Pending: return promise_t([this, on_fulfilled](promise_t npm) { - add_on_fulfilled(gen_on_fulfilled(on_fulfilled, npm)); + function ret; + gen_on_fulfilled(on_fulfilled, npm, ret); + add_on_fulfilled(ret); add_on_rejected([this, npm]() {npm.reject(reason);}); }); case State::Fulfilled: return promise_t([this, on_fulfilled](promise_t npm) { - gen_on_fulfilled(on_fulfilled, npm)(); + function ret; + gen_on_fulfilled(on_fulfilled, npm, ret); + ret(); }); case State::Rejected: return promise_t([this](promise_t npm) {npm.reject(reason);}); @@ -197,20 +230,24 @@ namespace promise { } } - template - promise_t fail(function on_rejected) { + template + promise_t fail(FuncRejected on_rejected) { switch (state) { case State::Pending: return promise_t([this, on_rejected](promise_t npm) { - add_on_rejected(gen_on_rejected(on_rejected, npm)); + function ret; + gen_on_rejected(on_rejected, npm, ret); + add_on_rejected(ret); add_on_fulfilled([this, npm]() {npm.resolve(result);}); }); case State::Fulfilled: return promise_t([this](promise_t npm) {npm.resolve(result);}); case State::Rejected: return promise_t([this, on_rejected](promise_t npm) { - add_on_rejected(gen_on_rejected(on_rejected, npm)); + function ret; + gen_on_rejected(on_rejected, npm, ret); + ret(); }); default: PROMISE_ERR_INVALID_STATE; } @@ -224,6 +261,7 @@ namespace promise { state = State::Fulfilled; //fulfilled_callback(); for (const auto &cb: fulfilled_callbacks) cb(); + rejected_callbacks.clear(); break; default: break; @@ -238,6 +276,7 @@ namespace promise { state = State::Rejected; //rejected_callback(); for (const auto &cb: rejected_callbacks) cb(); + rejected_callbacks.clear(); break; default: break; @@ -253,17 +292,17 @@ namespace promise { return promise_t([=] (promise_t npm) { size_t idx = 0; for (const auto &pm: promise_list) { - pm.then(function( + pm.then_any( [results, size, idx, npm](pm_any_t result) { (*results)[idx] = result; if (!--(*size)) npm.resolve(*results); - return none; - }), function( + return pm_any_t(none); + }, [npm, size](pm_any_t reason) { npm.reject(reason); - return none; - })); + return pm_any_t(none); + }); idx++; } }); @@ -275,19 +314,19 @@ namespace promise { template inline void promise_t::reject(T reason) const { ptr->reject(reason); } - template - inline promise_t promise_t::then(function fulfilled_callback, - function rejected_callback) const { + template + inline promise_t promise_t::then_any(FuncFulfilled fulfilled_callback, + FuncRejected rejected_callback) const { return ptr->then(fulfilled_callback, rejected_callback); } - template - inline promise_t promise_t::then(function fulfilled_callback) const { + template + inline promise_t promise_t::then_any(FuncFulfilled fulfilled_callback) const { return ptr->then(fulfilled_callback); } - template - inline promise_t promise_t::fail(function rejected_callback) const { + template + inline promise_t promise_t::fail_any(FuncRejected rejected_callback) const { return ptr->fail(rejected_callback); } @@ -301,44 +340,54 @@ namespace promise { using type = promise_t; }; - template - inline promise_t promise_t::then(function on_fulfilled) const { - using ret_type = typename callback_type_conversion::type; - return ptr->then(function( + template + struct callback_types { + using arg_type = typename function_traits::arg_type; + using ret_type = typename callback_type_conversion::ret_type>::type; + }; + + template + inline promise_t promise_t::then(FuncFulfilled on_fulfilled) const { + using arg_type = typename callback_types::arg_type; + using ret_type = typename callback_types::ret_type; + return ptr->then( [on_fulfilled](pm_any_t _result) { try { - return ret_type(on_fulfilled(std::any_cast(_result))); + return ret_type(on_fulfilled(std::any_cast(_result))); } catch (std::bad_any_cast e) { PROMISE_ERR_MISMATCH_TYPE; } - })); + }); } - template - inline promise_t promise_t::then(function on_fulfilled, - function on_rejected) const { - using fulfill_ret_type = typename callback_type_conversion::type; - using reject_ret_type = typename callback_type_conversion::type; - return ptr->then(function( + template + inline promise_t promise_t::then(FuncFulfilled on_fulfilled, + FuncRejected on_rejected) const { + using fulfill_arg_type = typename callback_types::arg_type; + using fulfill_ret_type = typename callback_types::ret_type; + using reject_arg_type = typename callback_types::arg_type; + using reject_ret_type = typename callback_types::ret_type; + return ptr->then( [on_fulfilled](pm_any_t _result) { try { - return fulfill_ret_type(on_fulfilled(std::any_cast(_result))); + return fulfill_ret_type(on_fulfilled(std::any_cast(_result))); } catch (std::bad_any_cast e) { PROMISE_ERR_MISMATCH_TYPE; } - }), function( + }, [on_rejected](pm_any_t _reason) { try { - return reject_ret_type(on_rejected(std::any_cast(_reason))); + return reject_ret_type(on_rejected(std::any_cast(_reason))); } catch (std::bad_any_cast e) { PROMISE_ERR_MISMATCH_TYPE; } - })); + }); } - template - inline promise_t promise_t::fail(function on_rejected) const { - using ret_type = typename callback_type_conversion::type; - return ptr->fail(function( + template + inline promise_t promise_t::fail(FuncRejected on_rejected) const { + using arg_type = typename callback_types::arg_type; + using ret_type = typename callback_types::ret_type; + return ptr->fail( [on_rejected](pm_any_t _reason) { try { - return ret_type(on_rejected(std::any_cast(_reason))); + return ret_type(on_rejected(std::any_cast(_reason))); } catch (std::bad_any_cast e) { PROMISE_ERR_MISMATCH_TYPE; } - })); + }); } } #endif diff --git a/test.cpp b/test.cpp index 327f5a6..2719340 100644 --- a/test.cpp +++ b/test.cpp @@ -12,25 +12,26 @@ int main() { puts("pm1"); //t1 = [pm]() {pm.reject(5);}; t1 = [pm]() {pm.resolve(5);}; - }).then(std::function([](int x) { + }).then([](int x) { printf("%d\n", x); return 6; - })).then(std::function([](int y) { + }).then([](int y) { printf("%d\n", y); return 0; - })).then(std::function([&t2](int x) { + }).then([&t2](int x) { auto pm2 = promise_t([x, &t2](promise_t pm2) { printf("pm2 %d\n", x); t2 = [pm2]() {pm2.resolve(std::string("hello"));}; }); return pm2; - })).then(std::function([](std::string s) { + }).then([](std::string s) { printf("%s\n", s.c_str()); return 10; - })).then(std::function([](int x) { + }).then([](int x) { printf("%d\n", x); return promise::none; - })); + }); + auto p1 = promise_t([&t4](promise_t pm) { puts("p1"); t4 = [pm]() {pm.resolve(1);}; @@ -47,25 +48,22 @@ int main() { }); auto p4 = promise::all(std::vector{p1, p2, p3}) - .then(std::function( - [](const promise::values_t values) { + .then([](const promise::values_t values) { printf("%d %s %s\n", std::any_cast(values[0]), std::any_cast(values[1]).c_str(), std::any_cast(values[2]).c_str()); - return 100; - })); + return 100; + }); auto p5 = promise::all(std::vector{pm, p4}) - .fail(std::function( - [](int reason) { - printf("reason: %d\n", reason); - return reason; - })) - .then(std::function( - [](const promise::values_t values) { - printf("finally %d\n", std::any_cast(values[1])); - return promise::none; - })); + .fail([](int reason) { + printf("reason: %d\n", reason); + return reason; + }) + .then([](const promise::values_t values) { + printf("finally %d\n", std::any_cast(values[1])); + return promise::none; + }); puts("calling t"); t4(); puts("calling t2"); -- cgit v1.2.3-70-g09d2