【问题标题】:How do I return a native object from a class derived from Nan::ObjectWrap?如何从派生自 Nan::ObjectWrap 的类返回本机对象?
【发布时间】:2018-01-17 13:17:57
【问题描述】:

我有两个简单的类 AB 我试图在 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 { ... }

每个分别包含AB 的实例。有了这些,我可以从 javascript 中创建一个 A 类型的对象,但是我在实现 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 构建。

【问题讨论】:

    标签: javascript c++ node.js node.js-nan


    【解决方案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 在另一个 question 的建议下,然后是我自己的一些实验。

    【讨论】:

      猜你喜欢
      • 2016-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-19
      • 2010-10-08
      • 1970-01-01
      相关资源
      最近更新 更多