Skip to content

Instantly share code, notes, and snippets.

@2bbb
Created March 20, 2018 06:49
Show Gist options
  • Save 2bbb/5252e91611411ac2ffa8e56d0591cb94 to your computer and use it in GitHub Desktop.
Save 2bbb/5252e91611411ac2ffa8e56d0591cb94 to your computer and use it in GitHub Desktop.
promise-js.cpp
#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