日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

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

综合教程

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

發布時間:2023/12/13 综合教程 26 生活家
生活随笔 收集整理的這篇文章主要介紹了 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++的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。