欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 综合教程 >内容正文

综合教程

Google V8编程详解(五)JS调用C++

发布时间:2023/12/13 综合教程 36 生活家
生活随笔 收集整理的这篇文章主要介绍了 Google V8编程详解(五)JS调用C++ 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

http://blog.csdn.net/feiyinzilgd/article/details/8453230

最近由于忙着解决个人单身的问题,时隔这么久才更新第五章。

上一章主要讲了Google V8的Context概念。那么其实Google V8的基本概念还有FunctionTemplate, ObjectTemplate等比较重要的基本概念,这些概念将在后续章节中进行渗透。

本章主要来讲讲如何通过V8来实现JS调用C++。JS调用C++,分为JS调用C++函数(全局),和调用C++类。

JS调用C++函数

JS调用C++函数,就是通过FunctionTemplate和ObjectTemplate进行扩展的。

FunctionTemplate,ObjectTemplate可以理解为JS function和C++ 函数之间的binding。FunctionTemplate实现了JS函数和C++函数的绑定,当然这种绑定是单向的,只能实现JS调用C++的函数。说的更直白一点,FunctionTemplate和ObjectTemplate就相当于JS的function和object。

基本原理就是先将C++ 函数通过FunctionTemplate实现绑定,然后将这个FunctionTemplate注册到JS的global上去,这样,JS就可以调用C++函数了。

代码如下:

上面这段代码实现了在JS调用C++ Yell()函数。

基本步骤分为A, B , C三步:

[cpp]view plaincopyprint?

#include"v8.h"
#include<string.h>
#include<stdio.h>

usingnamespacev8;
usingnamespacestd;


Handle<Value>Yell(constArguments&args){
HandleScopehandle_scope;
charbuffer[4096];

memset(buffer,0,sizeof(buffer));
Handle<String>str=args[0]->ToString();
str->WriteAscii(buffer);
printf("Yell:%s
",buffer);

returnUndefined();
}

intmain(intargc,char**argv){
HandleScopehandle_scope;

//A
Handle<FunctionTemplate>fun=FunctionTemplate::New(Yell);

//B
Handle<ObjectTemplate>global=ObjectTemplate::New();
global->Set(String::New("yell"),fun);

//C
Persistent<Context>cxt=Context::New(NULL,global);

Context::Scopecontext_scope(cxt);
Handle<String>source=String::New("yell('GoogleV8!')");
Handle<Script>script=Script::Compile(source);
Handle<Value>result=script->Run();

cxt.Dispose();
}

第一步,定义一个FunctionTempte并与C++函数绑定:

[cpp]view plaincopyprint?

Handle<FunctionTemplate>fun=FunctionTemplate::New(Yell);


第二部,定义一个ObectTemplate,并向该对象注册一个FunctionTemplate

[cpp]view plaincopyprint?

Handle<ObjectTemplate>global=ObjectTemplate::New();
global->Set(String::New("yell"),fun);


第三部,将该对象注册到JS的global中去:

[cpp]view plaincopyprint?

Persistent<Context>cxt=Context::New(NULL,global);

JS调用C++类

JS其实是无法直接使用C++类的,当JS中new一个对象的时候,需要手动将C++产生的对象同JS的对象进行绑定。从而就造成了JS使用C++类的假象:

[javascript]view plaincopyprint?

varcloudapp=newCloudApp();
cloudapp.xxInterface();

这一点V8做的不够强大,而Qt的QML(类JS脚本语言)就能实现自动绑定。

InternalField

当JS new一个对象的时候,C++中也会同步的new一个对象并将该指针保存在C++内部,并维护这个指针list,这就是V8 InternalField的作用。所有需要跟JS绑定的C++指针都存在这个InternalField中,其实就是一个list,一个V8 Object可以拥有任意数量的InternalField。如果需要使用保存在InterField中的C++指针,直接Get出来即可:

将C++指针封装到InternalField中:

[cpp]view plaincopyprint?

//....
void*ptr=...
object->SetInternalField(0,External::New(ptr));

上面这段代码将一个C++指针ptr保存在InternalField的index 0处。然后将来的某个时候如果需要获取这个指针,只需使用index 0来获取该指针。

将C++指针从InternalField中获取出来:

[cpp]view plaincopyprint?

Local<External>wrap=Local<External>::Cast(object->GetInternalField(0));
void*ptr=wrap->Value();

object->GetInternalField(0)就是从InternalField取出index=0处的C++指针。

External

既然说到C++指针的绑定,就必须说一下V8的External了。V8的External就是专门用来封装(Wrap)和解封(UnWrap)C++指针的。V8的External 实现如下:

[cpp]view plaincopyprint?

Local<Value>External::Wrap(void*value){
returnExternal::New(value);
}


void*External::Unwrap(Handle<v8::Value>obj){
returnExternal::Cast(*obj)->Value();
}

External其实就是C++指针的载体。这也就解释了前面在InternalField中设置和获取InternalField中的C++指针的时候,使用了External::New和wrap->Value()的原因了。External::Value()返回的就是C++指针。

下面开始上代码,看看究竟是如何实现JS调用C++类的:

[cpp]view plaincopyprint?

//C++Externtion
#include"v8.h"
#include"utils.h"

#include<iostream>
#include<string>

usingnamespacestd;

usingnamespacev8;

enumAppState{
IDEL=0,
LOADED,
STOP
};

classCloudApp{
public:
CloudApp(intid){
state=IDEL;
appId=id;
}
voidstart(){
cout<<"CloudAppbeenLoadedid="<<appId<<endl;
state=LOADED;
};

intgetState(){returnstate;}
intgetAppId(){returnappId;}

private:
AppStatestate;
intappId;
};

//向MakeWeak注册的callback.
voidCloudAppWeakReferenceCallback(Persistent<Value>object
,void*param){
if(CloudApp*cloudapp=static_cast<CloudApp*>(param)){
deletecloudapp;
}
}

//将C++指针通过External保存为Persistent对象,避免的指针被析构
Handle<External>MakeWeakCloudApp(void*parameter){
Persistent<External>persistentCloudApp=
Persistent<External>::New(External::New(parameter));

//MakeWeak非常重要,当JS世界new一个CloudApp对象之后
//C++也必须new一个对应的指针。
//JS对象析构之后必须想办法去析构C++的指针,可以通过MakeWeak来实现,
//MakeWeak的主要目的是为了检测PersistentHandle除了当前Persistent
//的唯一引用外,没有其他的引用,就可以析构这个PersistentHandle了,
//同时调用MakeWeak的callback。这是我们可以再这个callback中delete
//C++指针
persistentCloudApp.MakeWeak(parameter,CloudAppWeakReferenceCallback);

returnpersistentCloudApp;
}

//将JS传进来的参数解析之后,创建C++对象
CloudApp*NewCloudApp(constArguments&args){
CloudApp*cloudApp=NULL;

if(args.Length()==1){
cloudApp=newCloudApp(args[0]->ToInt32()->Value());
}else{
v8::ThrowException(String::New("ToomanyparametersforNewCloudApp"));
}

returncloudApp;
}

//相当于JS对应的构造函数,当JS中使用newCloudApp的时候,这个callback将自动被调用
Handle<Value>CloudAppConstructCallback(constArguments&args){
if(!args.IsConstructCall())
returnUndefined();

CloudApp*cloudapp=NewCloudApp(args);
Handle<Object>object=args.This();

object->SetInternalField(0,MakeWeakCloudApp(cloudapp));

returnUndefined();
}

Handle<Value>GetState(constArguments&args){
Handle<Object>self=args.Holder();

Local<External>wrap=Local<External>::Cast(self->GetInternalField(0));
void*ptr=wrap->Value();
CloudApp*cloudapp=static_cast<CloudApp*>(ptr);

returnInteger::New(cloudapp->getState());
}

Handle<Value>GetAppId(constArguments&args){
Handle<Object>self=args.Holder();

Local<External>wrap=Local<External>::Cast(self->GetInternalField(0));
void*ptr=wrap->Value();
CloudApp*cloudapp=static_cast<CloudApp*>(ptr);

returnInteger::New(cloudapp->getAppId());
}

Handle<Value>Start(constArguments&args){
Handle<Object>self=args.Holder();

Local<External>wrap=Local<External>::Cast(self->GetInternalField(0));
void*ptr=wrap->Value();
CloudApp*cloudapp=static_cast<CloudApp*>(ptr);

cloudapp->start();

returnUndefined();
}

voidSetupCloudAppInterface(Handle<ObjectTemplate>global){
Handle<FunctionTemplate>cloudapp_template=
FunctionTemplate::New(CloudAppConstructCallback);
cloudapp_template->SetClassName(String::New("CloudApp"));

Handle<ObjectTemplate>cloudapp_proto=cloudapp_template->PrototypeTemplate();
//这一步,完全可以使用cloudapp_inst->Set(....)
//使用prototype更符合JS编程
cloudapp_proto->Set(String::New("start"),FunctionTemplate::New(Start));
cloudapp_proto->Set(String::New("state"),FunctionTemplate::New(GetState));
cloudapp_proto->Set(String::New("appid"),FunctionTemplate::New(GetAppId));

//******很重要!!!
Handle<ObjectTemplate>cloudapp_inst=cloudapp_template->InstanceTemplate();
cloudapp_inst->SetInternalFieldCount(1);

//向JS世界注册一个函数,其本质就是向JS世界的global注册一个类。
//所以,也是通过向global注入CloudApp类。
global->Set(String::New("CloudApp"),cloudapp_template);
}

voidInitialnilizeInterface(Handle<ObjectTemplate>global){
SetupCloudAppInterface(global);
}

voidLoadJsAndRun(){
Handle<String>source=ReadJS("script.js");
Handle<Script>script=Script::Compile(source);
Handle<Value>result=script->Run();

printValue(result);
}

voidRegist2JsContext(Handle<ObjectTemplate>&object
,Persistent<Context>&context){
context=Context::New(NULL,object);
}

intmain(intargc,char**argv){
HandleScopehandle_scope;
Handle<ObjectTemplate>global=ObjectTemplate::New();
Persistent<Context>context;

InitialnilizeInterface(global);
Regist2JsContext(global,context);
Context::Scopecontext_scope(context);
LoadJsAndRun();

context.Dispose();

return0;
}


JS代码如下:

[javascript]view plaincopyprint?

//script.js
varcloudapp=newCloudApp(24);
cloudapp.start();
varresult;

上面的代码基本可以从函数名称和注释中明白是什么意思。最后再讲一点SetInternalFieldCount:

[cpp]view plaincopyprint?

Handle<ObjectTemplate>cloudapp_inst=cloudapp_template->InstanceTemplate();
cloudapp_inst->SetInternalFieldCount(1);


在其他的操作都就绪之后还必须SetInsternalFieldCount(),这一点是为了告诉V8,我们有几个InternalField,这里是只有1个。否则,在JS和C++指针交互过程中,V8在查找InternalField的时候会越界的。

版权申明:
转载文章请注明原文出处,任何用于商业目的,请联系本人:hyman_tan@126.com

总结

以上是生活随笔为你收集整理的Google V8编程详解(五)JS调用C++的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。