使用访问器访问 C++ 对象
为我们的类设置环境
怎样使用C++把一个类映射成为 JavaScript?首先来看看如下的例子:
//Sample class mapped to v8
class Point
{
public:
//constructor
Point(int x, int y):x_(x),y_(y){}
//internal class functions
//just increment x_
void Function_A(){++x_; }
//increment x_ by the amount
void Function_B(int vlr){x_+=vlr;}
//variables
int x_;
};
为了保证整个类映射后完全基于 JavaScript,我们对类的成员函数和变量都做映射。第一步,在上下文环境(context)中映射一个类模板:
Handle<FunctionTemplate> point_templ = FunctionTemplate::New();
point_templ->SetClassName(String::New("Point"));
从字面上似乎看出:我们只是创建了一个“函数(function)”模板,但实际上它能够被看作一个类。它的名字是“Point”,我们将会在后面用到。
然后,通过访问原型(prototype) 类模板,我们能为这个类添加内建的方法:
Handle<ObjectTemplate> point_proto = point_templ->PrototypeTemplate();
point_proto->Set("method_a", FunctionTemplate::New(PointMethod_A));
point_proto->Set("method_b", FunctionTemplate::New(PointMethod_B));
类能通过上面的代码“知道”它有两个方法。但这仍然是类的原型,我们必须要实例化这个类之后,才能使用这两个方法。
Handle<ObjectTemplate> point_inst = point_templ->InstanceTemplate();
point_inst->SetInternalFieldCount(1);
SetInternalFieldCount 函数为类指针申请空间(在后面会再一次提到)。一旦有了类的实例,我们就能为类中的变量添加访问器了:
point_inst->SetAccessor(String::New("x"), GetPointX, SetPointX);
至此,舞台算是搭好了,主角该上场了:
Point* p = new Point(0, 0);
新类已经建好了,但只能在C++中访问。要对其进行访问,我们需要:
Handle<Function> point_ctor = point_templ->GetFunction();
Local<Object> obj = point_ctor->NewInstance();
obj->SetInternalField(0, External::New(p));
GetFunction 返回 point 的构造器,有了它,我们可以使用 NewInstance 函数来创建一个新的实例。然后,使用类指针来设置内部域(这个域的空间我们在前面已经使用 SetInternalFieldCount 函数申请好了)。这样一来,JavaScript 就能通过指针访问该对象。
有一点我们漏掉了,我们想从 JavaScript 中访问它,但只有类模板和实例,没有名字:
context->Global()->Set(String::New("point"), obj);
最后一步将“point”这个名字和 obj 实例联系起来。到这里,我们仅仅是完成了在脚本中使用“point”名字来创建 Point 类的过程。
在 JavaScript 中访问类方法
这里讲的并不是我们怎样在 Point 类中访问 Function_A……
先来看看回调函数 PointMethod_A:
Handle<Value> PointMethod_A(const Arguments& args)
{
Local<Object> self = args.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<Point*>(ptr)->Function_A();
return Integer::New(static_cast<Point*>(ptr)->x_);
}
和普通的访问器类似,我们需对参数进行操作。而且,要能访问我们自己的类,还必须得从内部域中获取相关的类指针。在将内部域映射到“wrap”之后,就可以通过获取它的“值(Value)”来得到类指针,然后再进行类型转换。得到“point”类指针之后,可以通过它来调用 method_a ,method_a 来调用回调函数 PointMethod_A。
示例 v8_embedded_demo_with_object.zip (Visual C++ Express 2008 )完整的包含了上述各个步骤。
最后,我非常希望这篇文章能对你有所帮助。如果感觉文中有错误或者不清楚的地方,欢迎和我进行讨论~~