1. 程式人生 > >Google V8程式設計詳解(五)JS呼叫C++

Google V8程式設計詳解(五)JS呼叫C++

最近由於忙著解決個人單身的問題,時隔這麼久才更新第五章。

上一章主要講了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三步:

  1. #include "v8.h"
  2. #include <string.h>
  3. #include <stdio.h>
  4. usingnamespace v8;  
  5. usingnamespace std;  
  6. Handle<Value> Yell(const Arguments& args) {  
  7.     HandleScope  handle_scope;  
  8.     char buffer[4096];  
  9.     memset(buffer, 0, sizeof(buffer));  
  10.     Handle<String> str = args[0]->ToString();  
  11.     str->WriteAscii(buffer);  
  12.     printf("Yell: %s\n", buffer);  
  13.     return Undefined();  
  14. }  
  15. int main(int argc, char** argv) {  
  16.     HandleScope handle_scope;  
  17.     //A
  18.     Handle<FunctionTemplate> fun = FunctionTemplate::New(Yell);  
  19.     //B
  20.     Handle<ObjectTemplate> global = ObjectTemplate::New();  
  21.     global->Set(String::New("yell"), fun);  
  22.     //C
  23.     Persistent<Context> cxt = Context::New(NULL, global);  
  24.     Context::Scope context_scope(cxt);  
  25.     Handle<String> source = String::New("yell('Google V8!')");  
  26.     Handle<Script> script = Script::Compile(source);  
  27.     Handle<Value> result = script->Run();  
  28.     cxt.Dispose();  
  29. }  

第一步,定義一個FunctionTempte並與C++函式繫結:

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


第二部,定義一個ObectTemplate,並向該物件註冊一個FunctionTemplate

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


第三部,將該物件註冊到JS的global中去:

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

JS呼叫C++類

JS其實是無法直接使用C++類的,當JS中new一個物件的時候,需要手動將C++產生的物件同JS的物件進行繫結。從而就造成了JS使用C++類的假象:

  1. var cloudapp = new CloudApp();  
  2. 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中:

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

上面這段程式碼將一個C++指標ptr儲存在InternalField的index 0處。然後將來的某個時候如果需要獲取這個指標,只需使用index 0來獲取該指標。

將C++指標從InternalField中獲取出來:

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

object->GetInternalField(0)就是從InternalField取出index=0處的C++指標。

External

既然說到C++指標的繫結,就必須說一下V8的External了。V8的External就是專門用來封裝(Wrap)和解封(UnWrap)C++指標的。V8的External 實現如下:

  1. Local<Value> External::Wrap(void* value) {  
  2.   return External::New(value);  
  3. }  
  4. void* External::Unwrap(Handle<v8::Value> obj) {  
  5.   return External::Cast(*obj)->Value();  
  6. }  

External其實就是C++指標的載體。這也就解釋了前面在InternalField中設定和獲取InternalField中的C++指標的時候,使用了External::New和wrap->Value()的原因了。External::Value()返回的就是C++指標。

下面開始上程式碼,看看究竟是如何實現JS呼叫C++類的:

  1. //C++Externtion
  2. #include "v8.h"
  3. #include "utils.h"
  4. #include <iostream>
  5. #include <string>
  6. usingnamespace std;  
  7. usingnamespace v8;  
  8. enum AppState{  
  9.     IDEL = 0,  
  10.     LOADED,  
  11.     STOP  
  12. };  
  13. class CloudApp {  
  14. public:  
  15.     CloudApp(int id) {   
  16.         state = IDEL;  
  17.         appId = id;  
  18.     }  
  19.     void start() {  
  20.         cout << "CloudApp been Loaded id = " << appId << endl;  
  21.         state = LOADED;  
  22.     };  
  23.     int getState() { return state;}  
  24.     int getAppId() { return appId;}  
  25. private:  
  26.     AppState state;  
  27.     int appId;    
  28. };  
  29. //向MakeWeak註冊的callback.
  30. void CloudAppWeakReferenceCallback(Persistent<Value> object  
  31.                                                 , void * param) {  
  32.     if (CloudApp* cloudapp = static_cast<CloudApp*>(param)) {  
  33.         delete cloudapp;  
  34.     }  
  35. }  
  36. //將C++指標通過External儲存為Persistent物件,避免的指標被析構
  37. Handle<External> MakeWeakCloudApp(void* parameter) {  
  38.     Persistent<External> persistentCloudApp =   
  39.         Persistent<External>::New(External::New(parameter));  
  40. //MakeWeak非常重要,當JS世界new一個CloudApp物件之後
  41. //C++也必須new一個對應的指標。
  42. //JS物件析構之後必須想辦法去析構C++的指標,可以通過MakeWeak來實現,
  43. //MakeWeak的主要目的是為了檢測Persistent Handle除了當前Persistent 
  44. //的唯一引用外,沒有其他的引用,就可以析構這個Persistent Handle了,
  45. //同時呼叫MakeWeak的callback。這是我們可以再這個callback中delete 
  46. //C++指標
  47.     persistentCloudApp.MakeWeak(parameter, CloudAppWeakReferenceCallback);  
  48.     return persistentCloudApp;  
  49. }  
  50. //將JS傳進來的引數解析之後,建立C++物件
  51. CloudApp* NewCloudApp(const Arguments& args) {  
  52.     CloudApp* cloudApp = NULL;  
  53.     if (args.Length() == 1) {  
  54.         cloudApp = new CloudApp(args[0]->ToInt32()->Value());   
  55.     } else {  
  56.         v8::ThrowException(String::New("Too many parameters for NewCloudApp"));  
  57.     }  
  58.     return cloudApp;  
  59. }  
  60. //相當於JS對應的建構函式,當JS中使用new CloudApp的時候,這個callback將自動被呼叫
  61. Handle<Value> CloudAppConstructCallback(const Arguments& args) {  
  62.     if (!args.IsConstructCall())  
  63.         return Undefined();  
  64.     CloudApp* cloudapp = NewCloudApp(args);  
  65.     Handle<Object> object = args.This();  
  66.     object->SetInternalField(0, MakeWeakCloudApp(cloudapp));  
  67.     return Undefined();  
  68. }  
  69. Handle<Value> GetState(const Arguments& args) {  
  70.     Handle<Object> self = args.Holder();  
  71.     Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));  
  72.     void* ptr = wrap->Value();  
  73.     CloudApp* cloudapp = static_cast<CloudApp*>(ptr);  
  74.     return Integer::New(cloudapp->getState());  
  75. }  
  76. Handle<Value> GetAppId(const Arguments& args) {  
  77.     Handle<Object> self = args.Holder();  
  78.     Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));  
  79.     void* ptr = wrap->Value();  
  80.     CloudApp* cloudapp = static_cast<CloudApp*>(ptr);  
  81.     return Integer::New(cloudapp->getAppId());  
  82. }   
  83. Handle<Value> Start(const Arguments& args) {  
  84.     Handle<Object> self = args.Holder();  
  85.     Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));  
  86.     void* ptr = wrap->Value();  
  87.     CloudApp* cloudapp = static_cast<CloudApp*>(ptr);  
  88.     cloudapp->start();  
  89.     return Undefined();  
  90. }  
  91. void SetupCloudAppInterface(Handle<ObjectTemplate> global) {  
  92.     Handle<FunctionTemplate> cloudapp_template =   
  93.         FunctionTemplate::New(CloudAppConstructCallback);  
  94.     cloudapp_template->SetClassName(String::New("CloudApp"));  
  95. 相關推薦

    Google V8程式設計JS呼叫C++

    最近由於忙著解決個人單身的問題,時隔這麼久才更新第五章。 上一章主要講了Google V8的Context概念。那麼其實Google V8的基本概念還有FunctionTemplate, ObjectTemplate等比較重要的基本概念,這些概念將在後續章節中進行滲透。

    SpringMVC------參數綁定

    @override 占用 通過 問題 顯示 led prop -s 意義   參數綁定,簡單來說就是客戶端發送請求,而請求中包含一些數據,那麽這些數據怎麽到達 Controller ?這在實際項目開發中也是用到的最多的,那麽 SpringMVC 的參數綁定是怎麽實現的呢?下

    Spring------AOP

    利用 未來 bject ted r.java -c cti throw 位置   這章我們接著講 Spring 的核心概念---AOP,這也是 Spring 框架中最為核心的一個概念。   PS:本篇博客源碼下載鏈接:http://pan.baidu.com/s/1skZ

    CentOS 7.4 Tengine安裝配置

    tengine nginx https 十四、配置Tengine支持HTTPS1、演示環境:IP操作系統角色 192.168.1.222 CentOS 7.4 Tengine服務器 192.168.1.145 CentOS 6.9 自建CA服務器備註:Teng

    Zookeeper:通過JMX查看Zookeeper信息

    JMXJMX是對運行中的JAVA系統進行管控。目前ZK使用標準的JMX接口。修改ZK的啟動腳本zkServer.sh這個啟動腳本進行修改,第一句不是必須的,但是第二句是必須的在conf目錄下新建java.env文件重新啟動為什麽要在conf裏面建立一個java.env呢?其實你都寫在zkServer.sh中也

    unittest 引入裝飾器@classmethod

    ase 以及 testcase word ram lte 重新 username program 我們知道setUp()和setDown()的作用是在每條測試用例執行前準備測試環境以及用例測試結束後恢復測試環境,如果我們執行的測試類下所有測試用例的環境準備和環境復原的操作都

    Keepalived

    兩節點 監控mysql 需要 mysql sql weight 關閉 20px 依然 一.Keepalived集群中MASTER和BACKUP角色選舉策略 在keepalived集群中,其實並沒有嚴格意義上的主、備節點,雖然可以在keepalived配置文件中

    hashmap資料結構之HashMap、HashTable、ConcurrentHashMap 的區別

    【hashmap 與 hashtable】   hashmap資料結構詳解(一)之基礎知識奠基 hashmap資料結構詳解(二)之走進JDK原始碼 hashmap資料結構詳解(三)之hashcode例項及大小是2的冪次方解釋 hashmap資料結構詳解(四)之has

    PE檔案格式

    0x00 前言   前一篇瞭解了區塊虛擬地址和檔案地址轉換的相關知識,這一篇該把我們所學拿出來用用了。這篇我們將瞭解更為重要的一個知識點——輸入表和輸出表的知識。 0x01 輸入表   首先我們有疑問。這個輸入表是啥?為啥有輸入表?其實輸入表就是記錄PE輸入函式相

    安卓專案實戰之強大的網路請求框架okGo使用:擴充套件專案okRx,完美結合RxJava

    前言 在第一篇講解okGo框架新增依賴支援時,還記得我們額外新增的兩個依賴嗎,一個okRx和一個okServer,這兩個均是基於okGo框架的擴充套件專案,其中okRx可以使請求結合RxJava一起使用,而okServer則提供了強大的下載上傳功能,如斷點支援,多工管理等,本篇我們主要講

    java程式設計師菜鳥進階oracle基礎oracle資料庫體系架構

    分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

    【linux】Valgrind工具集:命令列

    一、使用方法 usage: valgrind [options] prog-and-args 使用方法:valgrind [引數選項] 程式和引數 二、選擇工具 tool-selection option, with default in [ ]: 工具選擇選項,預設值在[]

    mybatis ------動態SQL

    調用 otherwise efi 實例 其中 參數 sep 引用 完成 目錄 1、動態SQL:if 語句 2、動態SQL:if+where 語句 3、動態SQL:if+set 語句 4、動態SQL:choose(when,otherwise) 語句 5、動態SQL:tr

    JAVA設計模式----------狀態模式

    各位朋友,本次LZ分享的是狀態模式,在這之前,懇請LZ解釋一下,由於最近公司事情多,比較忙,所以導致更新速度稍微慢了些(哦,往後LZ會越來越忙=。=)。 狀態模式,又稱狀態物件模式(Pattern of Objects for States),狀態模式是物件的行為模式。

    C程式設計|用函式實現模組化程式設計

    目錄 一、為什麼要用函式 使用函式可使程式清晰、精煉、簡單、靈活。 函式就是功能。每一個函式用來實現一個特定的功能。函式名應反映其代表的功能。 在設計

    Linux 網路程式設計--------TCP狀態切換

    寫在前面: 正文: 一、TCP狀態切換 先貼一張TCP的狀態轉換圖,如下:   圖中:實線部分為主動發起的,虛線部分為被動相應的。 1、先來看主動發起連線請求(一般是client)的狀態變化:                            

    MFC下CSocket程式設計

    MFC下CSocket程式設計詳解:  1. 常用的函式和注意事項(詳細的函式介面說明請檢視MSDN):     CSocket::Create 初始化(一般寫伺服器程式都不要用為好,用下面的 CSocket::Socket 初始化)     CSocket::So

    Tkinter 元件:Frame

    Tkinter 元件詳解之Frame Frame(框架)元件是在螢幕上的一個矩形區域。Frame 主要是作為其他元件的框架基礎,或為其他元件提供間距填充。 何時使用 Frame 元件? Frame 元件主要用於在複雜的佈局中將其他元件分組,也用於填充間距和作為實現高階元件的基類。

    YOLO原始碼- YOLO中的7*7個grid和RPN中的9個anchors

    一直不知道7×7的網格到底是幹什麼的,不就是結果預測7×7×2個框嗎,這跟把原圖分成7×7有什麼關係?不分成7×7就不能預測7×7×2個框嗎? 之前跟一個朋友討論,他說7×7的網格是作為迴歸框的初始位置,我後來的很長一段時間一直這麼認為,後來想想不對啊

    Nginx ngx_http_proxy_module模組

    ngx_http_proxy_module 模組功能 轉發請求至另一臺主機 1、proxy_pass URL; 可用位置:location, if in location, limit_except 注意:proxy_pa