I'm new to NAPI, and I'm trying to convert and old Nan code to NAPI.
What happens is that I have a structure like this:
class PointWrapper : public Napi::ObjectWrap<PointWrapper> {
public:
static void init(Napi::Env env, Napi::Object exports);
PointWrapper(const Napi::CallbackInfo& info);
private:
Point point;
}
And I wrapped everything in the right way, so if I call on JS new Pointer(1, 2)
it'll instantiate a PointerWrapper
and set the right fields to Point
. So far, so good.
Now, the problem is that somewhere later I have a C++ code that wraps a Range
- a Range
is basically start
and end
, each containing a Point
.
I also have RangeWrapper
that does the same thing as PointWrapper
, but for range. This RangeWrapper
have a getStart
that basically needs to return a PointWrapper.
Now, how do I instantiate a PointWrapper
from RangeWrapper
? Basically, I want a constructor on PointWrapper
that, giving a Point
, I can get a PointWrapper
, all this in C++ and not on JS. Is it possible? Every code I saw tried to instantiate from inside PointWrapper
, never outside
Ok, so I found a solution - it's clumsy, but at least it works. The first thing I had to do was to make the "constructor" a variable that other places could use. So I changed my class to have a *constructor
pointer:
class PointWrapper : public Napi::ObjectWrap<PointWrapper> {
public:
static void init(Napi::Env env, Napi::Object exports);
PointWrapper(const Napi::CallbackInfo& info);
static Napi::FunctionReference *constructor; // <-- Added this
private:
Point point;
};
Then on my init
implementation (file point-wrapper.cpp
), I am using this constructor to "construct" the Point
:
Napi::FunctionReference *PointWrapper::constructor; // <-- Added this
void PointWrapper::init(Napi::Env env, Napi::Object exports) {
Napi::Function func = DefineClass(env, "Point", {
// methods, etc...
});
constructor = new Napi::FunctionReference(); // <-- Used it here
*constructor = Napi::Persistent(func); // <-- and here
exports.Set("Point", func);
}
So, to instantiate the PointWrapper
what I need is to call the constructor then unwrap things. Now, the class itself only have one possible constructor, that receives a Napi::CallbackInfo& info
. To instantiate this class with something from C++ side, we need to "wrap" a C++ object into Napi::External
:
// Supposing you have a point already created:
auto wrapped = Napi::External<Point>::New(env, &Point);
Napi::Value pointWrapperAsVal = PointWrapper::constructor->New({ wrapped });
Finally, you make the controller understand that it can be called with a Napi::External
object:
PointWrapper::PointWrapper(const Napi::CallbackInfo& info)
: Napi::ObjectWrap<PointWrapper>(info) {
if(info[0].IsExternal()) {
auto point = info[0].As<Napi::External<Point>>();
this->point = *point.Data();
} else {
// normal code
}
}
Remember that pointWrapperAsVal
is a Napi::Value
. You may need to convert to the right "type" if you need to use it from CPP - for example, with PointWrapper::Unwrap(pointWrapperAsVal)