Created
March 20, 2018 06:49
-
-
Save 2bbb/5252e91611411ac2ffa8e56d0591cb94 to your computer and use it in GitHub Desktop.
promise-js.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstddef> | |
#include <cstdint> | |
#include <type_traits> | |
namespace bbb { | |
namespace { | |
template <bool b, typename type> | |
using enable_if_t = typename std::enable_if<b, type>::type; | |
template <bool b, typename t, typename f> | |
using conditional_t = typename std::conditional<b, t, f>::type; | |
template <typename T> | |
using get_type = typename T::type; | |
template <typename embedding_type> | |
struct embedding { using type = embedding_type; }; | |
template <typename t> | |
struct defer { using type = t; }; | |
template <bool b, typename t, typename f> | |
using defered_conditional = conditional_t<b, defer<t>, defer<f>>; | |
template <typename t> | |
using resolve_t = get_type<t>; | |
}; | |
inline namespace integer_sequences { | |
template <typename type, type ... ns> | |
struct integer_sequence { | |
using value_type = type; | |
static constexpr std::size_t size() noexcept { return sizeof...(ns); } | |
}; | |
namespace detail { | |
template <typename integer_type, integer_type n, integer_type ... ns> | |
struct make_integer_sequence | |
: embedding<resolve_t<conditional_t< | |
n == 0, | |
defer<integer_sequence<integer_type, ns ...>>, | |
detail::make_integer_sequence<integer_type, n - 1, n - 1, ns ...> | |
>>> {}; | |
}; | |
template <typename type, type n> | |
using make_integer_sequence = get_type<detail::make_integer_sequence<type, n>>; | |
template <std::size_t ... ns> | |
using index_sequence = integer_sequence<std::size_t, ns ...>; | |
template <std::size_t n> | |
using make_index_sequence = make_integer_sequence<std::size_t, n>; | |
template <typename... types> | |
using index_sequence_for = make_index_sequence<sizeof...(types)>; | |
}; | |
}; | |
#include <type_traits> | |
#include <functional> | |
namespace bbb { | |
inline namespace function_traits_utils { | |
template <typename> | |
struct is_function | |
: std::false_type {}; | |
template <typename res, typename ... arguments> | |
struct is_function<std::function<res(arguments ...)>> | |
: std::true_type {}; | |
template <std::size_t index, typename ... arguments> | |
using type_at_t = typename std::tuple_element<index, std::tuple<arguments ...>>::type; | |
template <typename patient> | |
struct has_call_operator { | |
template <typename inner_patient, decltype(&inner_patient::operator())> struct checker {}; | |
template <typename inner_patient> static std::true_type check(checker<inner_patient, &inner_patient::operator()> *); | |
template <typename> static std::false_type check(...); | |
using type = decltype(check<patient>(nullptr)); | |
static constexpr bool value = type::value; | |
}; | |
namespace detail { | |
template <typename ret, typename ... arguments> | |
struct function_traits { | |
static constexpr std::size_t arity = sizeof...(arguments); | |
using result_type = ret; | |
using arguments_types_tuple = std::tuple<arguments ...>; | |
template <std::size_t index> | |
using argument_type = type_at_t<index, arguments ...>; | |
using raw_function_type = ret(arguments ...); | |
using function_type = std::function<ret(arguments ...)>; | |
template <typename function_t> | |
static constexpr function_type cast(function_t f) { | |
return static_cast<function_type>(f); | |
} | |
}; | |
}; | |
template <typename T> | |
struct function_traits : public function_traits<decltype(&T::operator())> {}; | |
template <typename class_type, typename ret, typename ... arguments> | |
struct function_traits<ret(class_type::*)(arguments ...) const> | |
: detail::function_traits<ret, arguments ...> {}; | |
template <typename class_type, typename ret, typename ... arguments> | |
struct function_traits<ret(class_type::*)(arguments ...)> | |
: detail::function_traits<ret, arguments ...> {}; | |
template <typename ret, typename ... arguments> | |
struct function_traits<ret(*)(arguments ...)> | |
: detail::function_traits<ret, arguments ...> {}; | |
template <typename ret, typename ... arguments> | |
struct function_traits<ret(arguments ...)> | |
: detail::function_traits<ret, arguments ...> {}; | |
template <typename ret, typename ... arguments> | |
struct function_traits<std::function<ret(arguments ...)>> | |
: detail::function_traits<ret, arguments ...> {}; | |
}; | |
}; | |
#include <iostream> | |
#include <vector> | |
#include <future> | |
#include <functional> | |
#include <memory> | |
namespace bbb { | |
template <typename result_type> | |
struct promise; | |
template<> | |
struct promise<void> { | |
struct defer { | |
defer() : promise() {}; | |
defer(defer &&) = default; | |
void resolve() | |
{ promise.set_value(0); } | |
void reject(std::exception_ptr e) | |
{ promise.set_exception(e); } | |
std::promise<uint8_t> promise; | |
}; | |
promise(std::function<void(defer)> callback) | |
: callback(callback) | |
, d() | |
, future(new std::future<std::uint8_t>(d.promise.get_future())) | |
, thread([=](defer d) { | |
try { | |
callback(std::move(d)); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
delete this; | |
}, std::move(d)) | |
{ thread.detach(); }; | |
promise(promise &&) = default; | |
virtual ~promise() { std::cout << "destruct " << typeid(decltype(*this)).name() << std::endl; }; | |
private: | |
template <typename new_result_type> | |
auto then_impl(std::function<new_result_type()> callback) | |
-> enable_if_t<!std::is_same<new_result_type, void>::value, promise<new_result_type> &> | |
{ | |
using new_promise = promise<new_result_type>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
future->get(); | |
d.resolve(callback()); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
)); | |
}; | |
promise<void> &then_impl(std::function<void()> callback) { | |
using new_promise = promise<void>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
future->get(); | |
callback(); | |
d.resolve(); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
)); | |
} | |
template <typename new_result_type> | |
auto then_impl( | |
std::function<new_result_type()> callback, | |
std::function<new_result_type(std::exception_ptr)> err_callback | |
) | |
-> enable_if_t<!std::is_same<new_result_type, void>::value, promise<new_result_type> &> | |
{ | |
using new_promise = promise<new_result_type>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, err_callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
future->get(); | |
d.resolve(callback()); | |
} catch(...) { | |
try { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.resolve(err_callback(err_ptr)); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
} | |
)); | |
}; | |
promise<void> &then_impl( | |
std::function<void()> callback, | |
std::function<void(std::exception_ptr)> err_callback | |
) { | |
using new_promise = promise<void>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, err_callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
future->get(); | |
callback(); | |
d.resolve(); | |
} catch(...) { | |
try { | |
std::exception_ptr err_ptr = std::current_exception(); | |
err_callback(err_ptr); | |
d.resolve(); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
} | |
)); | |
} | |
template <typename new_result_type> | |
auto except_impl(std::function<new_result_type(std::exception_ptr)> callback) | |
-> enable_if_t<!std::is_same<new_result_type, void>::value, promise<new_result_type> &> | |
{ | |
using new_promise = promise<new_result_type>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
future->get(); | |
d.resolve(callback(std::exception_ptr())); | |
} catch(...) { | |
try { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.resolve(callback(err_ptr)); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
} | |
)); | |
}; | |
promise<void> &except_impl(std::function<void(std::exception_ptr)> callback) { | |
using new_promise = promise<void>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
future->get(); | |
callback(std::exception_ptr()); | |
d.resolve(); | |
} catch(...) { | |
try { | |
std::exception_ptr err_ptr = std::current_exception(); | |
callback(err_ptr); | |
d.resolve(); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
} | |
)); | |
} | |
std::function<void(defer)> callback; | |
defer d; | |
std::shared_ptr<std::future<uint8_t>> future; | |
std::thread thread; | |
public: | |
template <typename function_type> | |
auto then(function_type callback) | |
-> enable_if_t< | |
has_call_operator<function_type>::value, | |
decltype(then_impl(function_traits<function_type>::cast(callback))) & | |
> | |
{ | |
return then_impl(function_traits<function_type>::cast(callback)); | |
}; | |
template <typename function_type, typename error_callback_type> | |
auto then(function_type &&callback, error_callback_type &&error_callback) | |
-> enable_if_t< | |
has_call_operator<function_type>::value | |
&& has_call_operator<error_callback_type>::value, | |
decltype(then_impl( | |
function_traits<function_type>::cast(callback), | |
function_traits<error_callback_type>::cast(error_callback) | |
)) & | |
> | |
{ | |
return then_impl(function_traits<function_type>::cast(callback)); | |
}; | |
template <typename function_type> | |
auto except(function_type callback) | |
-> enable_if_t< | |
has_call_operator<function_type>::value, | |
decltype(except_impl(function_traits<function_type>::cast(callback))) & | |
> | |
{ | |
return except_impl(function_traits<function_type>::cast(callback)); | |
}; | |
}; | |
template <typename result_type> | |
struct promise { | |
struct defer { | |
defer() : promise() {}; | |
defer(defer &&) = default; | |
void resolve(result_type data) | |
{ promise.set_value(data); } | |
void reject(std::exception_ptr e) | |
{ promise.set_exception(e); } | |
std::promise<result_type> promise; | |
}; | |
promise(std::function<void(defer)> callback) | |
: callback(callback) | |
, d() | |
, future(new std::future<result_type>(d.promise.get_future())) | |
, thread([=](defer d) { | |
try { | |
callback(std::move(d)); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
delete this; | |
}, std::move(d)) | |
{ thread.detach(); }; | |
promise(promise &&) = default; | |
virtual ~promise() { std::cout << "destruct " << typeid(decltype(*this)).name() << std::endl; }; | |
private: | |
template <typename new_result_type> | |
auto then_impl(std::function<new_result_type(result_type)> callback) | |
-> enable_if_t<!std::is_same<new_result_type, void>::value, promise<new_result_type> &> | |
{ | |
using new_promise = promise<new_result_type>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
d.resolve(callback(future->get())); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
)); | |
} | |
promise<void> &then_impl(std::function<void(result_type)> callback) { | |
using new_promise = promise<void>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
callback(future->get()); | |
d.resolve(); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
)); | |
} | |
template <typename new_result_type> | |
auto then_impl( | |
std::function<new_result_type(result_type)> callback, | |
std::function<new_result_type(std::exception_ptr)> err_callback | |
) | |
-> enable_if_t<!std::is_same<new_result_type, void>::value, promise<new_result_type> &> | |
{ | |
using new_promise = promise<new_result_type>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, err_callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
d.resolve(callback(future->get())); | |
} catch(...) { | |
try { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.resolve(err_callback(err_ptr)); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
} | |
)); | |
} | |
promise<void> &then_impl( | |
std::function<void(result_type)> callback, | |
std::function<void(std::exception_ptr)> err_callback | |
) { | |
using new_promise = promise<void>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, err_callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
callback(future->get()); | |
d.resolve(); | |
} catch(...) { | |
try { | |
std::exception_ptr err_ptr = std::current_exception(); | |
err_callback(err_ptr); | |
d.resolve(); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
} | |
)); | |
} | |
template <typename new_result_type> | |
auto except_impl(std::function<new_result_type(result_type)> callback) | |
-> enable_if_t<!std::is_same<new_result_type, void>::value, promise<new_result_type> &> | |
{ | |
using new_promise = promise<new_result_type>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
future->get(); | |
d.resolve(callback(std::exception_ptr())); | |
} catch(...) { | |
try { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.resolve(callback(err_ptr)); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
} | |
)); | |
} | |
promise<void> &except_impl(std::function<void(result_type)> callback) { | |
using new_promise = promise<void>; | |
auto future = this->future; | |
return *(new new_promise( | |
[callback, future](typename new_promise::defer d) { | |
try { | |
future->wait(); | |
future->get(); | |
callback(std::exception_ptr()); | |
d.resolve(); | |
} catch(...) { | |
try { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.resolve(callback(err_ptr)); | |
} catch(...) { | |
std::exception_ptr err_ptr = std::current_exception(); | |
d.reject(err_ptr); | |
} | |
} | |
} | |
)); | |
} | |
std::function<void(defer)> callback; | |
defer d; | |
std::shared_ptr<std::future<result_type>> future; | |
std::thread thread; | |
public: | |
template <typename function_type> | |
auto then(function_type &&callback) | |
-> enable_if_t< | |
has_call_operator<function_type>::value, | |
decltype(then_impl(function_traits<function_type>::cast(callback))) & | |
> | |
{ | |
return then_impl(function_traits<function_type>::cast(callback)); | |
}; | |
template <typename function_type, typename error_callback_type> | |
auto then(function_type &&callback, error_callback_type &&error_callback) | |
-> enable_if_t< | |
has_call_operator<function_type>::value | |
&& has_call_operator<error_callback_type>::value, | |
decltype(then_impl( | |
function_traits<function_type>::cast(callback), | |
function_traits<error_callback_type>::cast(error_callback) | |
)) & | |
> | |
{ | |
return then_impl( | |
function_traits<function_type>::cast(callback), | |
function_traits<error_callback_type>::cast(error_callback) | |
); | |
}; | |
template <typename function_type> | |
auto except(function_type callback) | |
-> enable_if_t< | |
has_call_operator<function_type>::value, | |
decltype(except_impl(function_traits<function_type>::cast(callback))) & | |
> | |
{ | |
return except_impl(function_traits<function_type>::cast(callback)); | |
}; | |
}; | |
template <typename result_type> | |
static promise<result_type> &resolve(result_type arg) { | |
return *(new promise<result_type>( | |
[=](typename promise<result_type>::defer d) { | |
d.resolve(arg); | |
} | |
)); | |
} | |
}; | |
#include <iostream> | |
#include <typeinfo> | |
struct exception : std::exception { | |
exception(std::string w) : w(w) {}; | |
std::string w; | |
virtual const char* what() const noexcept { return w.c_str(); }; | |
}; | |
int main(int argc, char *argv[]) { | |
std::promise<int> promise; | |
std::future<int> future = promise.get_future(); | |
try { | |
bbb::resolve(4) | |
.then([](int x) { | |
std::this_thread::sleep_for(std::chrono::milliseconds(500)); | |
std::cout << "1st " << x << std::endl; | |
throw exception("A"); | |
return x * 2; | |
}) | |
.then( | |
[](int x) { | |
std::this_thread::sleep_for(std::chrono::milliseconds(500)); | |
std::cout << "2nd " << x << std::endl; | |
// throw exception("B"); | |
return x * 2; | |
}, | |
[](std::exception_ptr errp) { | |
try { | |
std::rethrow_exception(errp); | |
} catch(std::exception &e) { | |
std::cerr << "2nd catch except " << e.what() << std::endl; | |
} | |
return 3; | |
} | |
) | |
.then([&](int x) { | |
std::this_thread::sleep_for(std::chrono::milliseconds(500)); | |
std::cout << "3rd " << x << std::endl; | |
throw exception("C"); | |
promise.set_value(x * 2); | |
}) | |
.except([&](std::exception_ptr ptr) { | |
std::cout << "except" << std::endl; | |
try { | |
std::rethrow_exception(ptr); | |
} catch(std::exception &err) { | |
std::cerr << err.what() << std::endl; | |
promise.set_value(-1); | |
} | |
}); | |
} catch(std::exception &e) { | |
std::cout << e.what() << std::endl; | |
} | |
std::cout << future.get() << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment