node.jsで次のようなことをしたいのですが...
var a = 新しい A(); var b = 新しい B();
//onTick は、引数として B のインスタンスを取る関数でなければなりません
a.onTick = function(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());
}
これが、プロパティのゲッターとセッターを設定する方法だと思います
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 は特別なことは何もしませんが、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) を作成する方法をまだ見つけようとしています。ここではクロージャーが有効だとは思いません。重要なのは、この関数には OnTick 関数として設定する必要があるため、1 つの引数 (WrappedClassB* wcb) しかないということです。