Как вернуть нативный объект из класса, производного от Nan::ObjectWrap?
У меня есть два простых класса A
а также B
что я пытаюсь выставить в родном модуле в node.js. A
непосредственно создается, но B
создается только путем вызова A::foo()
,
class Internal {};
class B {
public:
Internal internal;
explicit B(Internal internal):internal(internal){}
};
class A {
public:
A() : internal() {};
B foo() { return B(internal); }
private:
Internal internal;
};
Я хочу иметь возможность написать:
const M = require('node_nan_minimal');
const a = new M.A();
const b = a.foo();
Для этого я создаю два класса-обёртки, полученных из Nan::ObjectWrap
class AWrapper : public Nan::ObjectWrap { ... }
class BWrapper : public Nan::ObjectWrap { ... }
Каждый содержит экземпляр A
или же B
соответственно. С их помощью я могу создать объект типа A из Javascript, но у меня возникли проблемы с реализацией AWrapper::foo
,
static NAN_METHOD(foo) {
AWrapper* obj = Nan::ObjectWrap::Unwrap<AWrapper>(info.Holder());
B b = obj->a_.foo();
BWrapper * result = new BWrapper(b);
// Something to get a B object to javascript
// ...
// info.GetReturnValue().Set(result->Wrap());
// ...
// doesn't work - so what should it be?
}
Что я делаю, чтобы эта функция работала?
Полный код файла.cc
#include <node.h>
#include <nan.h>
class Internal {
};
class B {
public:
Internal internal;
explicit B(Internal internal):internal(internal){}
};
class A {
public:
A() : internal() {};
B foo() { return B(internal); }
private:
Internal internal;
};
class BWrapper : public Nan::ObjectWrap {
public:
B b_;
explicit BWrapper(B b) : b_(b) {}
~BWrapper() {}
};
class AWrapper : public Nan::ObjectWrap {
public:
A a_;
explicit AWrapper(A a) : a_(a) {}
~AWrapper() {}
static void register_class(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
tpl->SetClassName(Nan::New("A").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
Nan::SetPrototypeMethod(tpl, "foo", foo);
constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked());
Nan::Set(target, Nan::New("A").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());
}
private:
static NAN_METHOD(New) {
if (info.IsConstructCall()) {
A a;
AWrapper *obj = new AWrapper(a);
obj->Wrap(info.This());
info.GetReturnValue().Set(info.This());
} else {
const int argc = 1;
v8::Local<v8::Value> argv[argc] = {info[0]};
v8::Local<v8::Function> cons = Nan::New(constructor());
info.GetReturnValue().Set(cons->NewInstance(argc, argv));
}
}
static NAN_METHOD(foo) {
AWrapper* obj = Nan::ObjectWrap::Unwrap<AWrapper>(info.Holder());
B b = obj->a_.foo();
BWrapper * result = new BWrapper(b);
// Something to get a B object to javascript
//...
//info.GetReturnValue().Set(result->Wrap());
}
static inline Nan::Persistent<v8::Function> & constructor() {
static Nan::Persistent<v8::Function> my_constructor;
return my_constructor;
}
};
NAN_MODULE_INIT(InitModule) {
AWrapper::register_class(target);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, InitModule);
И полный репозиторий примера можно найти по адресу https://github.com/mikeando/node_nan_minimal который вы сможете клонировать, а затем собрать, используя npm install
,
1 ответ
Один из способов сделать эту работу:
- добавьте конструктор в BWrapper, но не предоставляйте эту функцию javascript. Заставьте эту функцию взять указатель на B. Это нужно будет сохранить в
init_class
функция. - добавьте функцию в BWrapper, чтобы создать новый экземпляр, используя этот конструктор. Это также примет указатель на B.
- сделать
foo
функция вызоваBWrapper::NewInstance
,
Это составляет следующие дополнения
class BWrapper {
...
static void init_class() {
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
tpl->SetClassName(Nan::New("B").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked());
}
static NAN_METHOD(New) {
if (!info.IsConstructCall()) {
return Nan::ThrowError("File() must be called as a constructor");
}
if (info.Length() != 1 || ! info[0]->IsExternal()) {
return Nan::ThrowError("File() can only be called internally");
}
B* b = static_cast<B*>(info[0].As<v8::External>()->Value());
BWrapper *obj = new BWrapper(*b);
obj->Wrap(info.This());
info.GetReturnValue().Set(info.This());
}
static v8::Local<v8::Object> NewInstance(B* b) {
Nan::EscapableHandleScope scope;
const unsigned argc = 1;
v8::Local<v8::Value> argv[argc] = { Nan::New<v8::External>(b) };
v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor());
v8::Local<v8::Object> instance = cons->NewInstance(argc, argv);
return scope.Escape(instance);
}
static inline Nan::Persistent<v8::Function> & constructor() {
static Nan::Persistent<v8::Function> my_constructor;
return my_constructor;
}
}
И изменения в AWrapper::foo
являются:
static NAN_METHOD(foo) {
AWrapper* obj = Nan::ObjectWrap::Unwrap<AWrapper>(info.Holder());
B b = obj->a_.foo();
info.GetReturnValue().Set(BWrapper::NewInstance(&b));
}
И, наконец, нам нужно убедиться, что класс зарегистрирован.
NAN_MODULE_INIT(InitModule) {
BWrapper::init_class();
AWrapper::register_class(target);
}
Я подозреваю, что это не самый чистый способ сделать это, и я хотел бы видеть любые альтернативы. Меня особенно интересует, есть ли у этого конструктора BWrapper какие-либо недостатки.
Частично это было взято из прочтения https://github.com/tracelytics/node-traceview-bindings/blob/master/src/metadata.cc#L18 предложения другого вопроса, а затем из моих собственных экспериментов.