Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Calling a function that uses a wrapped object as an argument in Node.js and v8

Calling a function that uses a wrapped object as an argument in Node.js and v8

Problem

I would like to do something like the following in node.js...

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

//onTick should be a Function that takes an instance of B as an argument

a.onTick = function(bInst){ .... }

a.loop();

meaning that A has a property "onTick" which is a function that gets called inside the loop. Note that A and B are defined as C++ wrapped functions, here are the definitions

void AClass::Init(Handle target) {
  Local 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::New(tpl->GetFunction());
  constructor->InstanceTemplate()->SetAccessor(String::New("onTick"), GetOnTick, SetOnTick);
  target->Set(String::NewSymbol("A"), constructor);
}

Handle 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 AClass::Loop(const Arguments &args) {
  HandleScope scope;
  AClass* acls = ObjectWrap::Unwrap(args.This());
  acls->wrappedInst_->loop();
  return scope.Close(Undefined());
}

I believe this is how you set the getter and setter of a property

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

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

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

What I am trying to do is take the function from setting onTick (which takes an instance of Class B) and wrap it inside a function that instatiates a new BClass.

Anyways heres the definition for BClass

Persistent BClass::constructor;
BClass::BClass() {
}
BClass::~BClass() {
}

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

Handle 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 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<:value>::New(bcls));
}

Both AClass and BClass use another C++ class and save the instance as a property (wrappedBInst, wrappedAInst) I believe I need the Instantiate function for when I need to convert an instance of the WrappedBClass into a BClass.

WrappedBClass doesn't do anything special but WrappedAClass inherits a class which has a loop and an onTick function, and the onTick function is where I need to call my Javascript function, so in WrappedAClass I overrode onTick and added a setTickFunc function.

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

So the only way I think that I can get into the loop and use a javascript function as the onTick function is to first wrap the javascript function into a c++ function and then set that function by calling setTickFunc(). Am I going about this the right way?

I'm a decent programmer but just recently started working with C++ so excuse my obvious mistakes, the biggest one is most likely this:

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

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

I'm still trying to figure out how to create an anonymous function that holds on to the value of a variable from the outside (acls). I don't think closures are valid here, the key is that this function only have one argument (WrappedClassB* wcb) because it needs to be set as the OnTick function.

Problem courtesy of: user2189354

Solution

You don't have to create anonymous function in C++. Maybe you can define your WrappedAClass like this.

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

Pay attention to the SetOnTick function, the second parameter is of type Local not Local. C++, not like js, is static typed language. Maybe you can define your SetOnTick Setter lick this:

void SetOnTick(Local property, Local value, const AccessorInfo& info){
  AClass* acls = ObjectWrap::Unwrap(info.Holder());
  if (value->IsFunction())
    acls->wrappedAInst_->setTickFunc(Local::Cast(value));
}
Solution courtesy of: Kevin

Discussion

View additional discussion.



This post first appeared on Node.js Recipes, please read the originial post: here

Share the post

Calling a function that uses a wrapped object as an argument in Node.js and v8

×

Subscribe to Node.js Recipes

Get updates delivered right to your inbox!

Thank you for your subscription

×