Skip to content

Commit ecf47f2

Browse files
committed
Make Function::New more usable
So far the Function::New overloads were too unspecific so that the compiler couldn't figure out the difference between them for most cases (i.e. when passing a simple function rather than a `std::function` object in). Resolve this by evil template magic that de-duplicates some of the code and does the right thing depending on whether it sees a `void` return value or not.
1 parent 7f5649e commit ecf47f2

File tree

2 files changed

+56
-82
lines changed

2 files changed

+56
-82
lines changed

napi-inl.h

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -898,47 +898,66 @@ inline const T* TypedArray_<T,A>::Data() const {
898898
// Function class
899899
////////////////////////////////////////////////////////////////////////////////
900900

901-
inline Function Function::New(napi_env env,
902-
VoidFunctionCallback cb,
903-
const char* utf8name,
904-
void* data) {
905-
// TODO: Delete when the function is destroyed
906-
VoidFunctionCallbackData* callbackData = new VoidFunctionCallbackData({ cb, data });
901+
// Helpers to handle functions exposed from C++.
902+
namespace details {
907903

908-
// TODO: set the function name
909-
napi_value value;
910-
napi_status status = napi_create_function(
911-
env, utf8name, VoidFunctionCallbackWrapper, callbackData, &value);
912-
if (status != napi_ok) throw Error::New(env);
913-
return Function(env, value);
914-
}
904+
template <typename Callable, typename Return>
905+
struct CallbackData {
906+
static inline
907+
napi_value Wrapper(napi_env env, napi_callback_info info) {
908+
try {
909+
CallbackInfo callbackInfo(env, info);
910+
CallbackData* callbackData =
911+
static_cast<CallbackData*>(callbackInfo.Data());
912+
return callbackData->callback(callbackInfo);
913+
}
914+
NAPI_RETHROW_JS_ERROR(env)
915+
}
916+
917+
Callable callback;
918+
};
915919

920+
template <typename Callable>
921+
struct CallbackData<Callable, void> {
922+
static inline
923+
napi_value Wrapper(napi_env env, napi_callback_info info) {
924+
try {
925+
CallbackInfo callbackInfo(env, info);
926+
CallbackData* callbackData =
927+
static_cast<CallbackData*>(callbackInfo.Data());
928+
callbackData->callback(callbackInfo);
929+
return nullptr;
930+
}
931+
NAPI_RETHROW_JS_ERROR(env)
932+
}
933+
934+
Callable callback;
935+
};
936+
937+
} // namespace details
938+
939+
template <typename Callable>
916940
inline Function Function::New(napi_env env,
917-
FunctionCallback cb,
918-
const char* utf8name,
919-
void* data) {
941+
Callable cb,
942+
const char* utf8name) {
943+
typedef decltype(cb(CallbackInfo(nullptr, nullptr))) ReturnType;
944+
typedef details::CallbackData<Callable, ReturnType> CbData;
920945
// TODO: Delete when the function is destroyed
921-
FunctionCallbackData* callbackData = new FunctionCallbackData({cb, data});
946+
auto callbackData = new CbData({ cb });
922947

948+
// TODO: set the function name
923949
napi_value value;
924950
napi_status status = napi_create_function(
925-
env, utf8name, FunctionCallbackWrapper, callbackData, &value);
951+
env, utf8name, CbData::Wrapper, callbackData, &value);
926952
if (status != napi_ok) throw Error::New(env);
927953
return Function(env, value);
928954
}
929955

956+
template <typename Callable>
930957
inline Function Function::New(napi_env env,
931-
VoidFunctionCallback cb,
932-
const std::string& utf8name,
933-
void* data) {
934-
return New(env, cb, utf8name.c_str(), data);
935-
}
936-
937-
inline Function Function::New(napi_env env,
938-
FunctionCallback cb,
939-
const std::string& utf8name,
940-
void* data) {
941-
return New(env, cb, utf8name.c_str(), data);
958+
Callable cb,
959+
const std::string& utf8name) {
960+
return New(env, cb, utf8name.c_str());
942961
}
943962

944963
inline Function::Function() : Object() {
@@ -1017,27 +1036,6 @@ inline Object Function::New(const std::vector<napi_value>& args) const {
10171036
return Object(_env, result);
10181037
}
10191038

1020-
inline napi_value Function::VoidFunctionCallbackWrapper(napi_env env, napi_callback_info info) {
1021-
try {
1022-
CallbackInfo callbackInfo(env, info);
1023-
VoidFunctionCallbackData* callbackData =
1024-
reinterpret_cast<VoidFunctionCallbackData*>(callbackInfo.Data());
1025-
callbackData->callback(callbackInfo);
1026-
return nullptr;
1027-
}
1028-
NAPI_RETHROW_JS_ERROR(env)
1029-
}
1030-
1031-
inline napi_value Function::FunctionCallbackWrapper(napi_env env, napi_callback_info info) {
1032-
try {
1033-
CallbackInfo callbackInfo(env, info);
1034-
FunctionCallbackData* callbackData =
1035-
reinterpret_cast<FunctionCallbackData*>(callbackInfo.Data());
1036-
return callbackData->callback(callbackInfo);
1037-
}
1038-
NAPI_RETHROW_JS_ERROR(env)
1039-
}
1040-
10411039
////////////////////////////////////////////////////////////////////////////////
10421040
// Buffer<T> class
10431041
////////////////////////////////////////////////////////////////////////////////

napi.h

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,6 @@ namespace Napi {
4949
typedef TypedArray_<float, napi_float32_array> Float32Array;
5050
typedef TypedArray_<double, napi_float64_array> Float64Array;
5151

52-
// Functions exposed to JavaScript must conform to one these callback signatures.
53-
// These are std::function<> typedefs instead of function pointers to enable lambdas.
54-
// (See ObjectWrap<T> for callbacks used when wrapping entire classes.)
55-
typedef std::function<void(const CallbackInfo& info)> VoidFunctionCallback;
56-
typedef std::function<Value(const CallbackInfo& info)> FunctionCallback;
57-
5852
// A N-API C++ module's registration callback (init) function has this sinature.
5953
typedef void ModuleRegisterCallback(Env env, Object exports, Object module);
6054

@@ -323,22 +317,16 @@ namespace Napi {
323317

324318
class Function : public Object {
325319
public:
320+
// Callable must implement operator() accepting a const CallbackInfo&
321+
// and return either void or Value.
322+
template <typename Callable>
326323
static Function New(napi_env env,
327-
VoidFunctionCallback cb,
328-
const char* utf8name = nullptr,
329-
void* data = nullptr);
330-
static Function New(napi_env env,
331-
FunctionCallback cb,
332-
const char* utf8name = nullptr,
333-
void* data = nullptr);
334-
static Function New(napi_env env,
335-
VoidFunctionCallback cb,
336-
const std::string& utf8name,
337-
void* data = nullptr);
324+
Callable cb,
325+
const char* utf8name = nullptr);
326+
template <typename Callable>
338327
static Function New(napi_env env,
339-
FunctionCallback cb,
340-
const std::string& utf8name,
341-
void* data = nullptr);
328+
Callable cb,
329+
const std::string& utf8name);
342330

343331
Function();
344332
Function(napi_env env, napi_value value);
@@ -357,18 +345,6 @@ namespace Napi {
357345

358346
Object New(const std::initializer_list<napi_value>& args) const;
359347
Object New(const std::vector<napi_value>& args) const;
360-
361-
private:
362-
static napi_value VoidFunctionCallbackWrapper(napi_env env, napi_callback_info info);
363-
static napi_value FunctionCallbackWrapper(napi_env env, napi_callback_info info);
364-
365-
template <typename TCallback>
366-
struct CallbackData {
367-
TCallback callback;
368-
void* data;
369-
};
370-
typedef CallbackData<VoidFunctionCallback> VoidFunctionCallbackData;
371-
typedef CallbackData<FunctionCallback> FunctionCallbackData;
372348
};
373349

374350
template <typename T>

0 commit comments

Comments
 (0)