zoukankan      html  css  js  c++  java
  • 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三步:

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



    第一步,定义一个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++类的假象:

    [javascript] view plaincopyprint?
     
    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.   
    5.   
    6. void* External::Unwrap(Handle<v8::Value> obj) {  
    7.   return External::Cast(*obj)->Value();  
    8. }  

    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.   
    5. #include <iostream>  
    6. #include <string>  
    7.   
    8. using namespace std;  
    9.   
    10. using namespace v8;  
    11.   
    12. enum AppState{  
    13.     IDEL = 0,  
    14.     LOADED,  
    15.     STOP  
    16. };  
    17.   
    18. class CloudApp {  
    19. public:  
    20.     CloudApp(int id) {   
    21.         state = IDEL;  
    22.         appId = id;  
    23.     }  
    24.     void start() {  
    25.         cout << "CloudApp been Loaded id = " << appId << endl;  
    26.         state = LOADED;  
    27.     };  
    28.   
    29.     int getState() { return state;}  
    30.     int getAppId() { return appId;}  
    31.       
    32. private:  
    33.     AppState state;  
    34.     int appId;    
    35. };  
    36.   
    37. //向MakeWeak注册的callback.  
    38. void CloudAppWeakReferenceCallback(Persistent<Value> object  
    39.                                                 , void * param) {  
    40.     if (CloudApp* cloudapp = static_cast<CloudApp*>(param)) {  
    41.         delete cloudapp;  
    42.     }  
    43. }  
    44.   
    45. //将C++指针通过External保存为Persistent对象,避免的指针被析构  
    46. Handle<External> MakeWeakCloudApp(void* parameter) {  
    47.     Persistent<External> persistentCloudApp =   
    48.         Persistent<External>::New(External::New(parameter));  
    49.           
    50. //MakeWeak非常重要,当JS世界new一个CloudApp对象之后  
    51. //C++也必须new一个对应的指针。  
    52. //JS对象析构之后必须想办法去析构C++的指针,可以通过MakeWeak来实现,  
    53. //MakeWeak的主要目的是为了检测Persistent Handle除了当前Persistent   
    54. //的唯一引用外,没有其他的引用,就可以析构这个Persistent Handle了,  
    55. //同时调用MakeWeak的callback。这是我们可以再这个callback中delete   
    56. //C++指针  
    57.     persistentCloudApp.MakeWeak(parameter, CloudAppWeakReferenceCallback);  
    58.   
    59.     return persistentCloudApp;  
    60. }  
    61.   
    62. //将JS传进来的参数解析之后,创建C++对象  
    63. CloudApp* NewCloudApp(const Arguments& args) {  
    64.     CloudApp* cloudApp = NULL;  
    65.       
    66.     if (args.Length() == 1) {  
    67.         cloudApp = new CloudApp(args[0]->ToInt32()->Value());   
    68.     } else {  
    69.         v8::ThrowException(String::New("Too many parameters for NewCloudApp"));  
    70.     }  
    71.   
    72.     return cloudApp;  
    73. }  
    74.   
    75. //相当于JS对应的构造函数,当JS中使用new CloudApp的时候,这个callback将自动被调用  
    76. Handle<Value> CloudAppConstructCallback(const Arguments& args) {  
    77.     if (!args.IsConstructCall())  
    78.         return Undefined();  
    79.       
    80.     CloudApp* cloudapp = NewCloudApp(args);  
    81.     Handle<Object> object = args.This();  
    82.   
    83.     object->SetInternalField(0, MakeWeakCloudApp(cloudapp));  
    84.   
    85.     return Undefined();  
    86. }  
    87.   
    88. Handle<Value> GetState(const Arguments& args) {  
    89.     Handle<Object> self = args.Holder();  
    90.   
    91.     Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));  
    92.     void* ptr = wrap->Value();  
    93.     CloudApp* cloudapp = static_cast<CloudApp*>(ptr);  
    94.   
    95.     return Integer::New(cloudapp->getState());  
    96. }  
    97.   
    98. Handle<Value> GetAppId(const Arguments& args) {  
    99.     Handle<Object> self = args.Holder();  
    100.   
    101.     Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));  
    102.     void* ptr = wrap->Value();  
    103.     CloudApp* cloudapp = static_cast<CloudApp*>(ptr);  
    104.   
    105.     return Integer::New(cloudapp->getAppId());  
    106. }   
    107.   
    108. Handle<Value> Start(const Arguments& args) {  
    109.     Handle<Object> self = args.Holder();  
    110.   
    111.     Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));  
    112.     void* ptr = wrap->Value();  
    113.     CloudApp* cloudapp = static_cast<CloudApp*>(ptr);  
    114.   
    115.     cloudapp->start();  
    116.   
    117.     return Undefined();  
    118. }  
    119.   
    120. void SetupCloudAppInterface(Handle<ObjectTemplate> global) {  
    121.     Handle<FunctionTemplate> cloudapp_template =   
    122.         FunctionTemplate::New(CloudAppConstructCallback);  
    123.     cloudapp_template->SetClassName(String::New("CloudApp"));  
    124.   
    125.     Handle<ObjectTemplate> cloudapp_proto = cloudapp_template->PrototypeTemplate();  
    126.     //这一步,完全可以使用cloudapp_inst->Set(....)  
    127.     //使用prototype更符合JS编程  
    128.     cloudapp_proto->Set(String::New("start"), FunctionTemplate::New(Start));  
    129.     cloudapp_proto->Set(String::New("state"), FunctionTemplate::New(GetState));  
    130.     cloudapp_proto->Set(String::New("appid"), FunctionTemplate::New(GetAppId));  
    131.       
    132.     //******很重要!!!  
    133.     Handle<ObjectTemplate> cloudapp_inst = cloudapp_template->InstanceTemplate();  
    134.     cloudapp_inst->SetInternalFieldCount(1);  
    135.       
    136.     //向JS世界注册一个函数,其本质就是向JS世界的global注册一个类。  
    137.     //所以,也是通过向global注入CloudApp类。  
    138.     global->Set(String::New("CloudApp"), cloudapp_template);  
    139. }  
    140.   
    141. void InitialnilizeInterface(Handle<ObjectTemplate> global) {  
    142.     SetupCloudAppInterface(global);  
    143. }  
    144.   
    145. void LoadJsAndRun() {  
    146.     Handle<String> source = ReadJS("script.js");  
    147.     Handle<Script> script = Script::Compile(source);  
    148.     Handle<Value> result = script->Run();  
    149.   
    150.     printValue(result);  
    151. }  
    152.   
    153. void Regist2JsContext(Handle<ObjectTemplate>& object  
    154.                             , Persistent<Context>& context) {  
    155.     context = Context::New(NULL, object);  
    156. }  
    157.   
    158. int main(int argc, char** argv) {  
    159.     HandleScope handle_scope;  
    160.     Handle<ObjectTemplate> global = ObjectTemplate::New();  
    161.     Persistent<Context> context;  
    162.       
    163.     InitialnilizeInterface(global);  
    164.     Regist2JsContext(global, context);  
    165.     Context::Scope context_scope(context);  
    166.     LoadJsAndRun();  
    167.   
    168.     context.Dispose();  
    169.       
    170.     return 0;  
    171. }  


    JS代码如下:

    [javascript] view plaincopyprint?
     
    1. //script.js  
    2. var cloudapp = new CloudApp(24);  
    3. cloudapp.start();  
    4. var result;  

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

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


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

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

  • 相关阅读:
    【AtCoder】AtCoder Grand Contest 017 解题报告
    【洛谷2523】[HAOI2011] Problem c(DP水题)
    【洛谷1912】[NOI2009] 诗人小G(决策单调性优化DP)
    【AtCoder】AtCoder Grand Contest 018 解题报告
    【洛谷3237】[HNOI2014] 米特运输(哈希)
    【洛谷2217】[HAOI2007] 分割矩阵(DP水题)
    【洛谷1039】侦探推理(字符串模拟题)
    【AtCoder】AtCoder Grand Contest 019 解题报告
    【洛谷4965】薇尔莉特的打字机(假装有棵Trie树)
    【CF512D】Fox And Travelling(拓扑+树上背包)
  • 原文地址:https://www.cnblogs.com/MingZznet/p/3231107.html
Copyright © 2011-2022 走看看