-
Notifications
You must be signed in to change notification settings - Fork 464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ObjectWrap Factory? #744
Comments
I could not find the N-API version of this example: https://nodejs.org/api/addons.html#addons_factory_of_wrapped_objects Is it available or could someone pls share similar N-API example? |
Hi @bsrdjan , Thank you for your patience. I must have overlooked your first question. Firstly, I'm not sure why you are having problems with the I just did this on node-addon-examples master branch:
Regarding a node-addon-api example of factory of wrapped objects... Let me get back to you on that 😄 |
After looking
The alternative would be, for example, replacing line 8 with: var obj2 = new createObject.MyObject(20); [NB: Does this help / answer your question? Let us know if you have any additional questions! Thanks, Kevin |
Thank you very much Kevin, the problem was my N-API version. It was updated only in |
The given example works for my use-case and there are two details I am not sure about. In extended example, when SetInstance data called for both classes, the "TypeError: obj.plusOne is not a function" is raised if MyFactory::Init myobject.h #ifndef MYOBJECT_H
#define MYOBJECT_H
#include <napi.h>
class MyObject : public Napi::ObjectWrap<MyObject>
{
public:
static Napi::Object Init(Napi::Env env, Napi::Object exports);
static Napi::Object NewInstance(Napi::Env env, Napi::Value arg);
MyObject(const Napi::CallbackInfo &info);
private:
Napi::Value PlusOne(const Napi::CallbackInfo &info);
double counter_;
};
class MyFactory : public Napi::ObjectWrap<MyFactory>
{
public:
static Napi::Object Init(Napi::Env env, Napi::Object exports);
MyFactory(const Napi::CallbackInfo &info);
static Napi::FunctionReference constructor;
private:
Napi::Value CreateObject(const Napi::CallbackInfo &info);
};
#endif myobject.cc #include "myobject.h"
#include <napi.h>
#include <uv.h>
using namespace Napi;
Napi::Object
MyObject::Init(Napi::Env env, Napi::Object exports)
{
Napi::HandleScope scope(env);
Napi::Function func = DefineClass(
env, "MyObject", {
InstanceMethod("plusOne", &MyObject::PlusOne),
});
Napi::FunctionReference *constructor = new Napi::FunctionReference();
*constructor = Napi::Persistent(func);
env.SetInstanceData(constructor);
exports.Set("MyObject", func);
return exports;
}
MyObject::MyObject(const Napi::CallbackInfo &info)
: Napi::ObjectWrap<MyObject>(info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
this->counter_ = info[0].As<Napi::Number>().DoubleValue();
};
Napi::Object MyObject::NewInstance(Napi::Env env, Napi::Value arg)
{
Napi::EscapableHandleScope scope(env);
Napi::Object obj = env.GetInstanceData<Napi::FunctionReference>()->New({arg});
return scope.Escape(napi_value(obj)).ToObject();
}
Napi::Value MyObject::PlusOne(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
this->counter_ = this->counter_ + 1;
return Napi::Number::New(env, this->counter_);
} addon.cc #include <napi.h>
#include "myobject.h"
Napi::FunctionReference MyFactory::constructor;
Napi::Object MyFactory::Init(Napi::Env env, Napi::Object exports)
{
Napi::HandleScope scope(env);
Napi::Function func = DefineClass(env,
"MyFactory", {
InstanceMethod("createObject", &MyFactory::CreateObject),
});
// Error "TypeError: obj.plusOne is not a function" if MyFactory::Init
// comes after MyObject::Init in Register module and SetInstanceData
// called for MyFactory as well:
//
// Napi::FunctionReference *constructor = new Napi::FunctionReference();
// *constructor = Napi::Persistent(func);
// env.SetInstanceData(constructor);
// constructor->SuppressDestruct();
//
// Workaround:
constructor = Napi::Persistent(func);
constructor.SuppressDestruct();
exports.Set("MyFactory", func);
return exports;
}
Napi::Value MyFactory::CreateObject(const Napi::CallbackInfo &info)
{
return MyObject::NewInstance(info.Env(), info[0]);
}
MyFactory::MyFactory(const Napi::CallbackInfo &info)
: Napi::ObjectWrap<MyFactory>(info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
};
Napi::Object RegisterModule(Napi::Env env, Napi::Object exports)
{
MyObject::Init(env, exports);
MyFactory::Init(env, exports);
return exports;
}
NODE_API_MODULE(NODE_GYP_MODULE_NAME, RegisterModule) addon.js var addon = require("bindings")("addon");
var factory = new addon.MyFactory();
var obj = factory.createObject(10);
console.log(obj.plusOne()); // 11
console.log(obj.plusOne()); // 12
console.log(obj.plusOne()); // 13
var obj2 = factory.createObject(20);
console.log(obj2.plusOne()); // 21
console.log(obj2.plusOne()); // 22
console.log(obj2.plusOne()); // 23 |
The 2nd question is about following scenario. The MyFactory instance should be created with the object, to be used as initial value for factory created objects. In example below, the Napi::Object var addon = require("bindings")("addon");
var factory = new addon.MyFactory({ k1: 10 });
var obj = factory.createObject();
console.log(obj.plusOne()); // 11
console.log(obj.plusOne()); // 12
console.log(obj.plusOne()); // 13
var factory2 = new addon.MyFactory({ k1: 20 });
var obj2 = factory2.createObject();
console.log(obj2.plusOne()); // 21
console.log(obj2.plusOne()); // 22
console.log(obj2.plusOne()); // 23 How to pass this "initializer" object to MyObject constructor? Napi::Object MyObject::NewInstance(const Napi::CallbackInfo &info, Napi::Value arg)
{
Napi::Env env = info.Env();
Napi::EscapableHandleScope scope(env);
Napi::Object objInit = arg.As<Napi::Object>();
double counter = objInit.Get(Napi::String::New(env, "k1")).As<Napi::Number>().DoubleValue();
// how to call the MyObject constructor here?
//Napi::Object obj = env.GetInstanceData<Napi::FunctionReference>()->New({arg});
//return scope.Escape(napi_value(obj)).ToObject();
} |
Hi @bsrdjan , When using If you want to use Thanks, Kevin |
Thanks, it solved my issue. |
Sorry for reviving this old issue, but, is there any example out there for multiple factory methods? The current example (As mentioned above) will only work for one. I have to setup a whole new object, and use it each time? Or is there any cleaner method? Thanks! |
Hello @KevinEady, |
Hi @dennislogghe-tomtom , If you store an template <typename K>
inline Napi::Function GetInstanceData(Napi::Env env, K key) {
auto data = env.GetInstanceData<Napi::ObjectReference>();
if ( !data ) {
return Napi::Function();
}
return data->Get(key).template As<Napi::Function>();
}
template <typename K>
inline void AttachInstanceData(Napi::Env env, K key, Napi::Function constructor) {
auto data = env.GetInstanceData<Napi::ObjectReference>();
if ( !data ) {
data = new Napi::ObjectReference();
*data = Napi::Reference<Napi::Object>::New(Napi::Object::New(env), 1);
env.SetInstanceData(data);
}
data->Set(key, constructor);
} Then, you can use it like so (in the above example): Napi::Object MyObject::Init(Napi::Env env, Napi::Object exports) {
Napi::Function func = DefineClass(
env, "MyObject", {InstanceMethod("plusOne", &MyObject::PlusOne)});
AttachInstanceData(env, "MyObject", func);
exports.Set("MyObject", func);
return exports;
}
Napi::Object MyFactory::Init(Napi::Env env, Napi::Object exports)
{
Napi::HandleScope scope(env);
Napi::Function func = DefineClass(env,
"MyFactory", {
InstanceMethod("createObject", &MyFactory::CreateObject),
});
AttachInstanceData(env, "MyFactory", func);
exports.Set("MyFactory", func);
return exports;
} Napi::Object MyObject::NewInstance(Napi::Env env, Napi::Value arg) {
Napi::EscapableHandleScope scope(env);
Napi::Object obj = GetInstanceData(env, "MyObject").New({arg});
return scope.Escape(napi_value(obj)).ToObject();
} cc: @sigmasoldi3r / @bsrdjan Let me know if this helps! Thanks, Kevin |
@KevinEady works like a charm, thanks a lot for your quick response and the time you took to write up this clear code sample! I came across a comment that is referring to the same problem (added by @djulien in nodejs/node-addon-examples@4c3b781). Maybe it would be good to create a new example or extend an existing one? |
I could not get the ObjectWrap Factory example myobject.cc running, because
env.SetInstanceData()
andenv.GetInstanceData()
methods do not exist. Is there a working example?Trying to create the Child ObjectWrap object instance, I call the Child::Init method. The instance is created but the Child::Child constructor not called and the instance not properly initialised. How to create an initialised Child object instance?
The text was updated successfully, but these errors were encountered: