I want to to implement using a native NodeJS module that works like the javascript module below
class C1{
make(){return new C2()}
}
class C2{}
module.exports({C1, C2})
My attempt was, (among some other failed attempts)
// so.cpp
#include <napi.h>
class C1: public Napi::ObjectWrap<C1> {
public:
C1(const Napi::CallbackInfo &info);
static void Initialize(Napi::Env& env, Napi::Object& target);
Napi::Value make(const Napi::CallbackInfo &info);
};
class C2: public Napi::ObjectWrap<C2> {
static Napi::Function constructor;
public:
C2(const Napi::CallbackInfo &info);
static void Initialize(Napi::Env& env, Napi::Object& target);
static C2 *New(const std::initializer_list<napi_value>& args);
};
Napi::Function C2::constructor;
void C2::Initialize(Napi::Env& env, Napi::Object& target){
constructor = Napi::ObjectWrap<C2>::DefineClass(env, "C2", {});
target.Set("C2", constructor);
}
void C1::Initialize(Napi::Env& env, Napi::Object& target) {
Napi::Function constructor = Napi::ObjectWrap<C1>::DefineClass(env, "C1", {
Napi::ObjectWrap<C1>::InstanceMethod("make", &C1::make),
});
target.Set("C1", constructor);
}
C2 *C2::New(const std::initializer_list<napi_value>& args){
return Napi::ObjectWrap<C2>::Unwrap(constructor.New(args));
}
C2::C2(const Napi::CallbackInfo &info): Napi::ObjectWrap<C2>(info) {}
C1::C1(const Napi::CallbackInfo &info): Napi::ObjectWrap<C1>(info) {}
// make() {return new C2()}
Napi::Value C1::make(const Napi::CallbackInfo &info){
C2 *jsclat = C2::New({});
return jsclat->Value();
}
// module.exports = {C1, C2}
Napi::Object Init(Napi::Env env, Napi::Object exports)
{
C1::Initialize(env, exports);
C2::Initialize(env, exports);
return exports;
}
NODE_API_MODULE(so, Init);
When I try to use the compiled module from javascript Node is terminated
const m = require('bindings')('hello')
const c1 = new m.c1()
c1.make() // FATAL ERROR: Error::New napi_get_last_error_info
// Aborted
If you want the same environment as I used, here you have a dockerfile.
FROM node:13.8.0-alpine3.11
RUN apk add --upgrade git python3 g++ make
WORKDIR /opt
RUN git clone https://github.com/nodejs/node-addon-examples.git
WORKDIR /opt/node-addon-examples/1_hello_world/node-addon-api/
RUN npm install \
&& npm install --save-dev gyp
&& npx node-gyp configure
COPY so.cpp hello.cc
RUN npx node-gyp rebuild
You are not allowed to use constructor
outside the function that created it - it is an automatic v8::Local
reference that is allocated on the stack - you need a persistent reference to do this:
#include <napi.h>
class C1: public Napi::ObjectWrap<C1> {
public:
C1(const Napi::CallbackInfo &info);
static void Initialize(Napi::Env &env, Napi::Object &target);
Napi::Value make(const Napi::CallbackInfo &info);
};
class C2: public Napi::ObjectWrap<C2> {
static Napi::Function constructor;
public:
static Napi::FunctionReference *New;
C2(const Napi::CallbackInfo &info);
static void Initialize(Napi::Env& env, Napi::Object& target);
};
Napi::FunctionReference *C2::New;
void C2::Initialize(Napi::Env& env, Napi::Object& target){
Napi::Function constructor = Napi::ObjectWrap<C2>::DefineClass(env, "C2", {});
target.Set("C2", constructor);
New = new Napi::FunctionReference();
*New = Napi::Persistent(constructor);
}
void C1::Initialize(Napi::Env& env, Napi::Object& target) {
Napi::Function constructor = Napi::ObjectWrap<C1>::DefineClass(env, "C1", {
Napi::ObjectWrap<C1>::InstanceMethod("make", &C1::make),
});
target.Set("C1", constructor);
}
C2::C2(const Napi::CallbackInfo &info): Napi::ObjectWrap<C2>(info) {}
C1::C1(const Napi::CallbackInfo &info): Napi::ObjectWrap<C1>(info) {}
// make() {return new C2()}
Napi::Value C1::make(const Napi::CallbackInfo &info){
Napi::Value c2 = C2::New->New({});
return c2;
}
// module.exports = {C1, C2}
Napi::Object Init(Napi::Env env, Napi::Object exports)
{
C1::Initialize(env, exports);
C2::Initialize(env, exports);
return exports;
}
NODE_API_MODULE(so, Init);