Skip to content

Commit

Permalink
As wished by nodejs#804 (review)
Browse files Browse the repository at this point in the history
  • Loading branch information
Markus Klemm committed Nov 20, 2020
1 parent fe02642 commit 3e8a810
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 158 deletions.
164 changes: 164 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5078,6 +5078,170 @@ Addon<T>::DefineProperties(Object object,
}
#endif // NAPI_VERSION > 5

#if NAPI_VERSION > 4
#ifdef NAPI_CPP_EXCEPTIONS
template<typename T>
class GenericCallbackWrapper {
public:
using result_t = T;
using callback_t = std::function<void(std::future<T>)>;
using conversion_function_t = std::function<Napi::Value(const Napi::Env &, std::future<T> &&)>;

GenericCallbackWrapper(const Napi::Promise::Deferred &deferred, conversion_function_t conversion_function)
: internal_(std::make_shared<internal>(deferred, conversion_function)) {
internal_->set_up();
}

callback_t get_native_callback() {
return internal_->get_native_callback();
}

private:
struct internal : std::enable_shared_from_this<internal> {
Napi::Promise::Deferred deferred_;
Napi::ThreadSafeFunction function_;
std::future<T> result_;
conversion_function_t conversion_function_;

internal(const Napi::Promise::Deferred &deferred,
conversion_function_t conversion_function) : deferred_(deferred),
conversion_function_(conversion_function) {}

void set_up() {
auto resolver = Napi::Function::New(deferred_.Env(), [me = this->shared_from_this()]
(const auto &info) -> Napi::Value {
if (info.Env() != me->deferred_.Env())
throw std::logic_error("generic_callback_wrapper: Napi Environments not equal");
me->set_deferred();
return info.Env().Undefined();
});
function_ = Napi::ThreadSafeFunction::New(deferred_.Env(), resolver, "Generic callback wrapepr", 0, 1);
}

void set_deferred() {
try {
auto value = conversion_function_( deferred_.Env(), std::move(result_));
deferred_.Resolve(value);
} catch (std::exception &e) {
deferred_.Reject(Napi::Error::New(deferred_.Env(), e.what()).Value());
} catch (...) {
deferred_.Reject(Napi::Error::New(deferred_.Env()).Value());
}
}


callback_t get_native_callback() {
return [me = this->shared_from_this()](auto &&future) {
me->native_callback(std::forward<decltype(future)>(future));;
};
}

void native_callback(std::future<T> &&future) {
this->result_ = std::forward<decltype(future)>(future);
this->function_.BlockingCall();
function_.Release();
}
};

std::shared_ptr<internal> internal_;

};

template<typename T>
class GenericSubscriptionWrapper {
public:
using js_callback_t = Napi::Function;
using callback_t = std::function<void(std::shared_future<T>)>;
using conversion_function_t = std::function<Napi::Value(const Napi::Env &, const std::shared_future<T> &)>;
using unsubscribe_function_t = std::function<void(void)>;

GenericSubscriptionWrapper(const Napi::Promise::Deferred &deferred,
conversion_function_t conversion_function, js_callback_t js_function)
: internal_(std::make_shared<internal>(deferred, conversion_function, js_function)) {
internal_->set_up();
}

template<typename F>
void set_unsubscription_function(F&& unsubscribe_function) {
internal_->unsubscribe_function_ = std::forward<F>(unsubscribe_function);
}

callback_t get_native_callback() {
return internal_->get_native_callback();
}


private:
struct internal : std::enable_shared_from_this<internal> {
Napi::Promise::Deferred deferred_;
conversion_function_t conversion_function_;
Napi::Reference<Napi::Function> js_function_;
Napi::ThreadSafeFunction function_;
Napi::Function conversion_wrapper_;
std::shared_future<T> result_;
unsubscribe_function_t unsubscribe_function_;

internal(const Napi::Promise::Deferred &deferred,
conversion_function_t conversion_function, js_callback_t js_function) :
deferred_(deferred), conversion_function_(conversion_function),
js_function_(Napi::Persistent(js_function)) {}

void set_up() {
conversion_wrapper_ = Napi::Function::New(deferred_.Env(), [me = this->shared_from_this()]
(const auto &info) -> Napi::Value {
if (info.Env() != me->deferred_.Env())
throw std::logic_error("generic_callback_wrapper: Napi Environments not equal");

me->set_deferred();

return info.Env().Undefined();
});
function_ = Napi::ThreadSafeFunction::New(deferred_.Env(), conversion_wrapper_,
"Generic subscription wrapper", 1, 1);

auto unsubscribe_call = Napi::Function::New(deferred_.Env(),
[me = this->shared_from_this()](const auto &) {
me->unsubscribe_function_();
me->tear_down();
});
deferred_.Resolve(unsubscribe_call);
}

void tear_down() {
function_.Release();
}

callback_t get_native_callback() {
return [me = this->shared_from_this()](auto &&future) {
me->native_callback(std::forward<decltype(future)>(future));
};
}

void native_callback(std::shared_future<T> &&future) {
result_ = std::forward<decltype(future)>(future);
this->function_.BlockingCall();
}

void set_deferred() {
Napi::Value value;
try {
value = conversion_function_( deferred_.Env(), std::move(result_));
} catch (Napi::Error &e) {
value = e.Value();
} catch (std::exception &e) {
value = Napi::Error::New(deferred_.Env(), e.what()).Value();
} catch (...) {
value = Napi::Error::New(deferred_.Env()).Value();
}
js_function_.Value().Call({value});
}
};
std::shared_ptr<internal> internal_;
};

#endif //NAPI_CPP_EXCEPTIONS
#endif //NAPI_VERSION > 4

} // namespace Napi

#endif // SRC_NAPI_INL_H_
161 changes: 3 additions & 158 deletions napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -2408,165 +2408,10 @@ namespace Napi {

#ifdef NAPI_CPP_EXCEPTIONS
template<typename T>
class GenericCallbackWrapper {
public:
using result_t = T;
using callback_t = std::function<void(std::future<T>)>;
using conversion_function_t = std::function<Napi::Value(const Napi::Env &, std::future<T> &&)>;

GenericCallbackWrapper(const Napi::Promise::Deferred &deferred, conversion_function_t conversion_function)
: internal_(std::make_shared<internal>(deferred, conversion_function)) {
internal_->set_up();
}

callback_t get_native_callback() {
return internal_->get_native_callback();
}

private:
struct internal : std::enable_shared_from_this<internal> {
Napi::Promise::Deferred deferred_;
Napi::ThreadSafeFunction function_;
std::future<T> result_;
conversion_function_t conversion_function_;

internal(const Napi::Promise::Deferred &deferred,
conversion_function_t conversion_function) : deferred_(deferred),
conversion_function_(conversion_function) {}

void set_up() {
auto resolver = Napi::Function::New(deferred_.Env(), [me = this->shared_from_this()]
(const auto &info) -> Napi::Value {
if (info.Env() != me->deferred_.Env())
throw std::logic_error("generic_callback_wrapper: Napi Environments not equal");
me->set_deferred();
return info.Env().Undefined();
});
function_ = Napi::ThreadSafeFunction::New(deferred_.Env(), resolver, "Generic callback wrapepr", 0, 1);
}

void set_deferred() {
try {
auto value = conversion_function_( deferred_.Env(), std::move(result_));
deferred_.Resolve(value);
} catch (std::exception &e) {
deferred_.Reject(Napi::Error::New(deferred_.Env(), e.what()).Value());
} catch (...) {
deferred_.Reject(Napi::Error::New(deferred_.Env()).Value());
}
}


callback_t get_native_callback() {
return [me = this->shared_from_this()](auto &&future) {
me->native_callback(std::forward<decltype(future)>(future));;
};
}

void native_callback(std::future<T> &&future) {
this->result_ = std::forward<decltype(future)>(future);
this->function_.BlockingCall();
function_.Release();
}
};

std::shared_ptr<internal> internal_;

};

class GenericCallbackWrapper;
template<typename T>
class GenericSubscriptionWrapper {
public:
using js_callback_t = Napi::Function;
using callback_t = std::function<void(std::shared_future<T>)>;
using conversion_function_t = std::function<Napi::Value(const Napi::Env &, const std::shared_future<T> &)>;
using unsubscribe_function_t = std::function<void(void)>;

GenericSubscriptionWrapper(const Napi::Promise::Deferred &deferred,
conversion_function_t conversion_function, js_callback_t js_function)
: internal_(std::make_shared<internal>(deferred, conversion_function, js_function)) {
internal_->set_up();
}

template<typename F>
void set_unsubscription_function(F&& unsubscribe_function) {
internal_->unsubscribe_function_ = std::forward<F>(unsubscribe_function);
}

callback_t get_native_callback() {
return internal_->get_native_callback();
}


private:
struct internal : std::enable_shared_from_this<internal> {
Napi::Promise::Deferred deferred_;
conversion_function_t conversion_function_;
Napi::Reference<Napi::Function> js_function_;
Napi::ThreadSafeFunction function_;
Napi::Function conversion_wrapper_;
std::shared_future<T> result_;
unsubscribe_function_t unsubscribe_function_;

internal(const Napi::Promise::Deferred &deferred,
conversion_function_t conversion_function, js_callback_t js_function) :
deferred_(deferred), conversion_function_(conversion_function),
js_function_(Napi::Persistent(js_function)) {}

void set_up() {
conversion_wrapper_ = Napi::Function::New(deferred_.Env(), [me = this->shared_from_this()]
(const auto &info) -> Napi::Value {
if (info.Env() != me->deferred_.Env())
throw std::logic_error("generic_callback_wrapper: Napi Environments not equal");

me->set_deferred();

return info.Env().Undefined();
});
function_ = Napi::ThreadSafeFunction::New(deferred_.Env(), conversion_wrapper_,
"Generic subscription wrapper", 1, 1);

auto unsubscribe_call = Napi::Function::New(deferred_.Env(),
[me = this->shared_from_this()](const auto &) {
me->unsubscribe_function_();
me->tear_down();
});
deferred_.Resolve(unsubscribe_call);
}

void tear_down() {
function_.Release();
}

callback_t get_native_callback() {
return [me = this->shared_from_this()](auto &&future) {
me->native_callback(std::forward<decltype(future)>(future));
};
}

void native_callback(std::shared_future<T> &&future) {
result_ = std::forward<decltype(future)>(future);
this->function_.BlockingCall();
}

void set_deferred() {
Napi::Value value;
try {
value = conversion_function_( deferred_.Env(), std::move(result_));
} catch (Napi::Error &e) {
value = e.Value();
} catch (std::exception &e) {
value = Napi::Error::New(deferred_.Env(), e.what()).Value();
} catch (...) {
value = Napi::Error::New(deferred_.Env()).Value();
}
js_function_.Value().Call({value});
}
};
std::shared_ptr<internal> internal_;
};

#endif
class GenericSubscriptionWrapper;
#endif //NAPI_CPP_EXCEPTIONS

// Memory management.
class MemoryManagement {
Expand Down

0 comments on commit 3e8a810

Please sign in to comment.