在 Node.js 和 v8 中调用使用包装对象作为参数的函数

Posted

技术标签:

【中文标题】在 Node.js 和 v8 中调用使用包装对象作为参数的函数【英文标题】:Calling a function that uses a wrapped object as an argument in Node.js and v8 【发布时间】:2013-03-09 03:38:17 【问题描述】:

我想在 node.js 中执行以下操作...

var a = new A(); var b = new B();

//onTick 应该是一个以 B 的实例为参数的函数

a.onTick = 函数(bInst) ……

a.loop();

意味着 A 有一个属性“onTick”,它是一个在循环内被调用的函数。 注意 A 和 B 被定义为 C++ 包装的函数,这里是定义

void AClass::Init(Handle<Object> target) 
  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
  tpl->SetClassName(String::NewSymbol("A"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  tpl->PrototypeTemplate()->Set(String::NewSymbol("tick"),
      FunctionTemplate::New(Tick)->GetFunction());
  tpl->PrototypeTemplate()->Set(String::NewSymbol("loop"),
  FunctionTemplate::New(Loop)->GetFunction());

  constructor = Persistent<Function>::New(tpl->GetFunction());
  constructor->InstanceTemplate()->SetAccessor(String::New("onTick"), GetOnTick, SetOnTick);
  target->Set(String::NewSymbol("A"), constructor);


Handle<Value> AClass::New(const v8::Arguments &args) 
  HandleScope scope;
  AClass* acls = new AClass();
  WrappedAClass* wrappedA = new WrappedAClass();
  acls->wrappedAInst_ = wrappedA;
  window->Wrap(args.This());
  return args.This();

Handle<Value> AClass::Loop(const Arguments &args) 
  HandleScope scope;
  AClass* acls = ObjectWrap::Unwrap<AClass>(args.This());
  acls->wrappedInst_->loop();
  return scope.Close(Undefined());

我相信这就是您设置属性的 getter 和 setter 的方式

Handle<Function> GetOnTick(Local<String> property, const AccessorInfo& info) 
  AClass* acls = ObjectWrap::Unwrap<AClass>(info.Holder());
  return acls->onTick_;


void SetOnTick(Local<String> property, Local<Function> value, const AccessorInfo& info) 
  AClass* acls = ObjectWrap::Unwrap<AClass>(info.Holder());

  acls->onTick_ = Persistent<Function>::New(value);
  //Here's where I know I'm doing it wrong
  void func(WrappedClassB* wcb)  
    const unsigned argc = 1;
    Local<Value> argv[argc] = 
       Local<Value>::New(BClass::Instantiate(wcb)) ;
    acls->onTick_->Call(Context::GetCurrent()->Global(), argc, argv);   
  
  acls->wrappedAInst_->setTickFunc(func);

我要做的是从设置 onTick 中获取函数(它接受 B 类的一个实例),并将其包装在一个用于实例化新 BClass 的函数中。

无论如何这里是 BClass 的定义

Persistent<Function> BClass::constructor;
BClass::BClass() 

BClass::~BClass() 


void BClass::Init(Handle<Object> target) 
  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
  tpl->SetClassName(String::NewSymbol("B"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  constructor = Persistent<Function>::New(tpl->GetFunction());
  target->Set(String::NewSymbol("B"), constructor);


Handle<Value> BClass::New(const v8::Arguments &args) 
  HandleScope scope;
  BClass* bcls = new BClass();
  bcls->Wrap(args.This());
  WrappedBClass* wrappedB = new WrappedBClass();
  bcls->wrappedBInst_ = wrappedB;
  return args.This();

Handle<Value> BClass::Instantiate(const WrappedBClass &wbc) 
  HandleScope scope;
//I know the following is wrong but it shows what I am trying to do
  BClass* bcls = new BClass();
  bcls->wrappedBInst_ = wbc;
  return scope.Close(Local<v8::Value>::New(bcls));

AClass 和 BClass 都使用另一个 C++ 类并将实例保存为属性(wrappedBInst、wrappedAInst)我相信当我需要将 WrappedBClass 的实例转换为 BClass 时需要 Instantiate 函数。

WrappedBClass 没有做任何特别的事情,但 WrappedAClass 继承了一个具有循环和 onTick 函数的类,而 onTick 函数是我需要调用我的 javascript 函数的地方,所以在 WrappedAClass 我覆盖了 onTick 并添加了一个 setTickFunc 函数。

class WrappedAClass : public InheritedClass
public:
  void setTickFunc(void (*func)(WrappedBClass*))
    tickFunc = func;
  
protected:
  void tickFunc;
  virtual void onTick(WrappedBClass* wbc)
    if(tickFunc)
      tickFunc(wbc);
    
  

所以我认为我可以进入循环并使用 javascript 函数作为 onTick 函数的唯一方法是首先将 javascript 函数包装到 c++ 函数中,然后通过调用 setTickFunc() 设置该函数。我这样做的方式是否正确?

我是一个不错的程序员,但最近才开始使用 C++,所以请原谅我明显的错误,最大的错误很可能是这样的:

void SetOnTick(Local<String> property, Local<Function> value, const AccessorInfo& info) 
      AClass* acls = ObjectWrap::Unwrap<AClass>(info.Holder());

      acls->onTick_ = Persistent<Function>::New(value);
      //Here's where I know I'm doing it wrong
      void func(WrappedClassB* wcb)  
        const unsigned argc = 1;
        Local<Value> argv[argc] = 
           Local<Value>::New(BClass::Instantiate(wcb)) ;
        acls->onTick_->Call(Context::GetCurrent()->Global(), argc, argv);   
      
      acls->wrappedAInst_->setTickFunc(func);
    

我仍在试图弄清楚如何创建一个匿名函数来保存来自外部的变量的值 (acls)。我认为闭包在这里无效,关键是这个函数只有一个参数(WrappedClassB* wcb),因为它需要设置为 OnTick 函数。

【问题讨论】:

【参考方案1】:

您不必在 C++ 中创建匿名函数。也许你可以像这样定义你的WrappedAClass

class WrappedAClass : public InheritedClass
public:
  void setTickFunc(Local<Function> jsFn)
    HandleScope scope;
    jsTickFunc = Persistent<Function>::New(jsTickFunc);
  
protected:
  Persistent<Function> jsTickFunc;
  virtual void onTick(WrappedBClass* wbc)
    HandleScope scope;
    if(jsTickFunc.IsEmpty())
        return;
    const unsigned argc = 1;
    Local<Value> argv[argc] = 
     Local<Value>::New(BClass::Instantiate(wcb)) ;
    jsTickFunc->Call(Context::GetCurrent()->Global(), argc, argv);  
  

注意SetOnTick函数,第二个参数的类型是Local&lt;Value&gt;而不是Local&lt;Function&gt;。 C++ 与 js 不同,是静态类型语言。也许你可以定义你的SetOnTick Setter 舔这个:

void SetOnTick(Local<String> property, Local<Value> value, const AccessorInfo& info)
  AClass* acls = ObjectWrap::Unwrap<AClass>(info.Holder());
  if (value->IsFunction())
    acls->wrappedAInst_->setTickFunc(Local<Function>::Cast(value));

【讨论】:

以上是关于在 Node.js 和 v8 中调用使用包装对象作为参数的函数的主要内容,如果未能解决你的问题,请参考以下文章

Node.js——nodejs(内存控制)(转)

Learn Node.js

node.js

深入浅出Node.js - 内存控制

新 V8 即将推出和 Node.js

没有 v8-profiler 的 Node.js 内存泄漏搜索