TeaSpeak-Client/native/dist/ext_nan/NanEventCallback.h
2019-06-26 22:09:01 +02:00

207 lines
6.1 KiB
C++

#pragma once
#include <functional>
#include <tuple>
#include <memory>
#include <mutex>
#include <uv.h>
#include <nan.h>
namespace Nan {
namespace async_helper {
template <typename...>
struct callback_wrap;
template <typename...>
struct lambda_type;
template<typename lambda, class ret, class klass, class... Args>
struct lambda_type<lambda, ret(klass::*)(Args...) const > {
using void_function = std::function<void(Args...)>;
using result = callback_wrap<Args...>;
inline static void_function wrap(const std::shared_ptr<lambda>& lam) {
return [lam](Args&&... args) { (*lam)(std::forward<Args>(args)...); };
}
};
template<typename lambda, class ret, class klass, class... Args>
struct lambda_type<lambda, ret(klass::*)(Args...) > {
using void_function = std::function<void(Args...)>;
using result = callback_wrap<Args...>;
inline static void_function wrap(const std::shared_ptr<lambda>& lam) {
return [lam](Args&&... args) { (*lam)(std::forward<Args>(args)...); };
}
};
// helper class
template<typename R, template<typename...> class Params, typename... Args, std::size_t... I>
inline R call_helper(std::function<R(Args...)> const& func, Params<Args...> const& params, std::index_sequence<I...>) {
return func(std::forward<Args>((Args&&) std::get<I>(params))...);
}
template <typename... Args>
struct callback_args {
uv_async_t handle{};
bool flag_execute = false;
bool destroy_only = false;
bool option_destroy_run = false; /* execute the callback, even if the haven had been destoryed */
std::mutex destroy_lock;
std::function<void(Args...)> callback;
std::tuple<Args...> arguments;
};
template <bool...>
struct _or_ {
constexpr static bool value = false;
};
template <bool T, bool... Args>
struct _or_<T, Args...> {
constexpr static bool value = T || _or_<Args...>::value;
};
template <typename... Args>
struct callback_scoped {
callback_args<Args...>* handle;
std::function<void(Args&&...)> callback;
std::function<void()> destroy;
callback_scoped(
callback_args<Args...>* handle,
const std::function<void(Args &&...)> &callback,
std::function<void()> destroy
) : callback(callback), destroy(std::move(destroy)), handle(handle) {}
~callback_scoped() { destroy(); }
};
template <typename... Args>
struct callback_wrap {
std::shared_ptr<callback_scoped<Args...>> handle;
void call(Args&&... args, bool no_throw = false) {
if(!this->handle) {
if(no_throw)
return;
throw std::bad_function_call();
}
handle->callback(std::forward<Args>(args)...);
}
void operator()(Args&&... args) {
if(!this->handle)
throw std::bad_function_call();
handle->callback(std::forward<Args>(args)...);
}
void operator()(Args&&... args) const {
if(!this->handle)
throw std::bad_function_call();
handle->callback(std::forward<Args>(args)...);
}
callback_wrap<Args...>& operator=(const callback_wrap<Args...>& other) {
this->handle = other.handle;
return *this;
}
operator bool() const {
return this->handle != nullptr;
}
callback_wrap<Args...>& operator=(nullptr_t) {
this->handle = nullptr;
return *this;
}
/**
* @returns true if the callback will be called even
* if the handle has been destroyed
*/
bool option_destroyed_execute() const {
if(!this->handle)
std::__throw_logic_error("missing handle");
return this->handle->handle->option_destroy_run;
}
/**
* @param flag
* If true then the callback will be called event
* if the handle has already been destroyed
* @return this
*/
callback_wrap<Args...>& option_destroyed_execute(bool flag) {
if(!this->handle)
throw std::logic_error("missing handle");
this->handle->handle->option_destroy_run = flag;
return *this;
}
};
};
template <typename... Args>
using callback_t = async_helper::callback_wrap<Args...>;
template <typename... Args, bool referenced_args = async_helper::_or_<std::is_reference<Args>::value...>::value>
inline async_helper::callback_wrap<Args...> async_callback(std::function<void(Args...)> callback) {
static_assert(!referenced_args, "Argument references aren't allowed");
using callback_t = std::function<void(Args...)>;
using callback_args = async_helper::callback_args<Args...>;
auto args = new callback_args{};
args->callback = std::move(callback);
args->destroy_only = false;
memset(&args->handle, 0, sizeof(args->handle));
args->handle.data = args;
uv_async_init(Nan::GetCurrentEventLoop(), &args->handle, [](uv_async_t* async) {
auto _args = (callback_args*) async->data;
std::unique_lock destroy_lock(_args->destroy_lock); //This may changed after calling the function (deleted while invoking is an example)
if(!_args->destroy_only || (_args->option_destroy_run && _args->flag_execute)) {
_args->flag_execute = false;
auto arguments = std::move(_args->arguments); //Move the tuple now before it get overridden :)
destroy_lock.unlock();
async_helper::call_helper(_args->callback, arguments, std::index_sequence_for<Args...>{});
destroy_lock.lock();
}
if(_args->destroy_only) {
uv_close((uv_handle_t*) (void*) async, [](uv_handle_t* handle) {
delete (callback_args*) handle->data;
});
}
});
return {
std::make_shared<async_helper::callback_scoped<Args...>>(
args,
[args](Args&&... invoker_args) {
std::lock_guard lock(args->destroy_lock);
args->flag_execute = true;
args->arguments = std::tuple<Args...>{std::forward<Args>(invoker_args)...};
uv_async_send(&args->handle);
},
[args]{
std::lock_guard lock(args->destroy_lock);
args->destroy_only = true;
uv_async_send(&args->handle);
}
)
};
}
/* lambda edition */
template <typename lambda,
typename lambda_info = typename async_helper::lambda_type<lambda, decltype(&lambda::operator())>,
typename result = typename lambda_info::result>
inline result async_callback(lambda&& lam) {
auto handle = std::make_shared<lambda>(std::forward<lambda>(lam));
return async_callback(lambda_info::wrap(handle));
}
}