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<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());
}
I believe this is how you set the getter and setter of a property
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);
}
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<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));
}
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<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);
}
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.
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<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);
}
}
Pay attention to the SetOnTick function, the second parameter is of type Local<Value> not Local<Function>. C++, not like js, is static typed language. Maybe you can define your SetOnTick Setter lick this:
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));
}