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
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
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.
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));
}
Discussion
View additional discussion.