最近因为忙于解决个人独身问题,所以已经很久没有更新第五章了。
上一章主要讲了GoogleV8的Context概念。 所以很明显,GoogleV8的基本概念包括FunctionTemplate、ObjectTemplate等重要的基本概念,这些都会在后续章节中渗透。
本章主要讲如何通过V8实现JS调用C++。 JS调用C++,分为JS调用C++函数(全局)、调用C++类。
JS调用C++函数
JS调用C++函数,通过FunctionTemplate和ObjectTemplate进行扩展。
FunctionTemplate、ObjectTemplate可以理解为JSfunction和C++函数的绑定。 FunctionTemplate实现了JS函数和C++函数的绑定。 事实上,这些绑定是双向的,只能实现JS调用C++函数。 说得更直白一点javascript 调用flash,FunctionTemplate和ObjectTemplate就相当于JS的函数和对象。
基本原理是先通过FunctionTemplate绑定C++函数,然后将FunctionTemplate注册到JS全局,这样JS就可以调用C++函数了。
代码如下所示:
上一段代码实现了在JS中调用C++Yell()函数。
基本步骤分为三个步骤:A、B、C:
#include "v8.h" #include #include using namespace v8; using namespace std; Handle Yell(const Arguments& args) { HandleScope handle_scope; char buffer[4096]; memset(buffer, 0, sizeof(buffer)); Handle str = args[0]->ToString(); str->WriteAscii(buffer); printf("Yell: %sn", buffer); return Undefined(); } int main(int argc, char** argv) { HandleScope handle_scope; //A Handle fun = FunctionTemplate::New(Yell); //B Handle global = ObjectTemplate::New(); global->Set(String::New("yell"), fun); //C Persistent cxt = Context::New(NULL, global); Context::Scope context_scope(cxt); Handle source = String::New("yell('Google V8!')"); Handle script = Script::Compile(source); Handle result = script->Run(); cxt.Dispose(); }
第一步是定义一个 FunctionTempte 并将其绑定到 C++ 函数:
Handle fun = FunctionTemplate::New(Yell);
第二部分定义一个ObectTemplate并向该对象注册一个FunctionTemplate
Handle global = ObjectTemplate::New(); global->Set(String::New("yell"), fun);
第三步,在JS的全局中注册对象:
Persistent cxt = Context::New(NULL, global);
JS调用C++类
JS 似乎很难直接使用 C++ 类。 当JS中new一个对象时,需要自动将C++形成的对象绑定到JS对象上。 这导致了使用C++类的JS的出现:
var cloudapp = new CloudApp(); cloudapp.xxInterface();
V8在这方面还不够强大javascript 调用flash,而Qt的QML(类JS脚本语言)可以实现手动绑定。
内部场
当JSnew一个对象时,C++也会同步一个新对象并在C++内部保存指针,并维护指针列表,这就是V8InternalField的作用。 所有需要与JS绑定的C++指针都存在于这个InternalField中。 尽管它只是一个列表,但 V8Object 可以具有任意数量的 InternalField。 如果需要使用InterField中存储的C++指针,直接获取即可:
将C++表针封装到InternalField中:
//.... void* ptr = ... object->SetInternalField(0, External::New(ptr));
前面的代码将 C++ 指针 ptr 保存在 InternalField 的索引 0 处。 如果将来需要获取这个指针,只需使用index0来获取指针即可。
从InternalField获取C++监视指针:
Local wrap = Local::Cast(object->GetInternalField(0)); void* ptr = wrap->Value();
object->GetInternalField(0) 是从InternalField 中检索index=0 处的C++ 指针。
外部的
既然说到了C++指针的绑定,就必须要说一下V8的External。 V8的External专门用于包裹(Wrap)和展开(UnWrap)C++手表引脚。 V8的外部实现如下:
Local External::Wrap(void* value) { return External::New(value); } void* External::Unwrap(Handle obj) { return External::Cast(*obj)->Value(); }
外部似乎是C++手中的载体。 这也解释了上面在InternalField中设置和获取C++指针时使用External::New和wrap->Value()的动机。 external::Value() 返回 C++ 指针。
我们先从代码开始,看看JS是如何调用C++类的:
//C++Externtion #include "v8.h" #include "utils.h" #include #include using namespace std; using namespace v8; enum AppState{ IDEL = 0, LOADED, STOP }; class CloudApp { public: CloudApp(int id) { state = IDEL; appId = id; } void start() { cout << "CloudApp been Loaded id = " << appId << endl; state = LOADED; }; int getState() { return state;} int getAppId() { return appId;} private: AppState state; int appId; }; //向MakeWeak注册的callback. void CloudAppWeakReferenceCallback(Persistent object , void * param) { if (CloudApp* cloudapp = static_cast(param)) { delete cloudapp; } } //将C++指针通过External保存为Persistent对象,避免的指针被析构 Handle MakeWeakCloudApp(void* parameter) { Persistent persistentCloudApp = Persistent::New(External::New(parameter)); //MakeWeak非常重要,当JS世界new一个CloudApp对象之后 //C++也必须new一个对应的指针。 //JS对象析构之后必须想办法去析构C++的指针,可以通过MakeWeak来实现, //MakeWeak的主要目的是为了检测Persistent Handle除了当前Persistent //的唯一引用外,没有其他的引用,就可以析构这个Persistent Handle了, //同时调用MakeWeak的callback。这是我们可以再这个callback中delete //C++指针 persistentCloudApp.MakeWeak(parameter, CloudAppWeakReferenceCallback); return persistentCloudApp; } //将JS传进来的参数解析之后,创建C++对象 CloudApp* NewCloudApp(const Arguments& args) { CloudApp* cloudApp = NULL; if (args.Length() == 1) { cloudApp = new CloudApp(args[0]->ToInt32()->Value()); } else { v8::ThrowException(String::New("Too many parameters for NewCloudApp")); } return cloudApp; } //相当于JS对应的构造函数,当JS中使用new CloudApp的时候,这个callback将自动被调用 Handle CloudAppConstructCallback(const Arguments& args) { if (!args.IsConstructCall()) return Undefined(); CloudApp* cloudapp = NewCloudApp(args); Handle
JS代码如下:
//script.js var cloudapp = new CloudApp(24); cloudapp.start(); var result;
里面的代码基本上从函数名和注释就可以明白它的意思。 最后说一下SetInternalFieldCount:
Handle cloudapp_inst = cloudapp_template->InstanceTemplate(); cloudapp_inst->SetInternalFieldCount(1);
当其他操作准备好后,需要SetInsternalFieldCount()。 这是告诉V8我们有几个InternalField,但这里只有一个。 否则,JS和C++表针交互过程中,V8搜索InternalField时会出界。