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. using namespace v8;
  5. using namespace 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. using namespace std;
  7. using namespace 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. Handle<ObjectTemplate> cloudapp_proto = cloudapp_template->PrototypeTemplate();
  96. //这一步,完全可以使用cloudapp_inst->Set(....)
  97. //使用prototype更符合JS编程
  98. cloudapp_proto->Set(String::New("start"), FunctionTemplate::New(Start));
  99. cloudapp_proto->Set(String::New("state"), FunctionTemplate::New(GetState));
  100. cloudapp_proto->Set(String::New("appid"), FunctionTemplate::New(GetAppId));
  101. //******很重要!!!
  102. Handle<ObjectTemplate> cloudapp_inst = cloudapp_template->InstanceTemplate();
  103. cloudapp_inst->SetInternalFieldCount(1);
  104. //向JS世界注册一个函数,其本质就是向JS世界的global注册一个类。
  105. //所以,也是通过向global注入CloudApp类。
  106. global->Set(String::New("CloudApp"), cloudapp_template);
  107. }
  108. void InitialnilizeInterface(Handle<ObjectTemplate> global) {
  109. SetupCloudAppInterface(global);
  110. }
  111. void LoadJsAndRun() {
  112. Handle<String> source = ReadJS("script.js");
  113. Handle<Script> script = Script::Compile(source);
  114. Handle<Value> result = script->Run();
  115. printValue(result);
  116. }
  117. void Regist2JsContext(Handle<ObjectTemplate>& object
  118. , Persistent<Context>& context) {
  119. context = Context::New(NULL, object);
  120. }
  121. int main(int argc, char** argv) {
  122. HandleScope handle_scope;
  123. Handle<ObjectTemplate> global = ObjectTemplate::New();
  124. Persistent<Context> context;
  125. InitialnilizeInterface(global);
  126. Regist2JsContext(global, context);
  127. Context::Scope context_scope(context);
  128. LoadJsAndRun();
  129. context.Dispose();
  130. return 0;
  131. }

JS代码如下:

  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

Google V8编程详解(五)JS调用C++的更多相关文章

  1. Google V8编程详解附录

    Google V8编程详工具函数 头文件:utils.h #ifndef UTILS_H_ #define UTILS_H_ #include "v8.h" #include &l ...

  2. Google V8编程详解(序)Cloud App

    此系列文章转载于此http://blog.csdn.net/feiyinzilgd/article/details/8247723          应用程序发展到今天,应用程序的概念也在不断地发生着 ...

  3. Google V8编程详解(四)Context

    http://blog.csdn.net/feiyinzilgd/article/details/8266780 上一章,比较略提了下V8的Context.本章将详细的讲解下Context的概念以及用 ...

  4. Google V8编程详解(三)Handle & HandleScope

    上一章简单的演示了一个Helloworld Demo.里面涉及到了V8的一些基本类型和概念,本章将围绕这个Demo对V8的基本类型和相关概念进行讲解. 这里还是先把Demo贴出来便于后面分析: #in ...

  5. Google V8编程详解(二)HelloWorld

    转自http://blog.csdn.net/feiyinzilgd/article/details/8248448 上一章讲到了V8的编译和安装,这一章开始从一个demo着手. 这里选用了官方文档的 ...

  6. Google V8编程详解(一)V8的编译安装(Ubuntu)

    V8的编译比较简单,需要同时安装git和svn. 下载V8源码: git clone git://github.com/v8/v8.git v8 && cd v8 切换到最新版本: g ...

  7. Linux 网络编程详解五(TCP/IP协议粘包解决方案二)

    ssize_t recv(int s, void *buf, size_t len, int flags); --与read相比,只能用于网络套接字文件描述符 --当flags参数的值设置为MSG_P ...

  8. ORACLE PL/SQL编程详解

    ORACLE PL/SQL编程详解 编程详解 SQL语言只是访问.操作数据库的语言,并不是一种具有流程控制的程序设计语言,而只有程序设计语言才能用于应用软件的开发.PL /SQL是一种高级数据库程序设 ...

  9. 前端后台以及游戏中使用Google Protocol Buffer详解

    前端后台以及游戏中使用Google Protocol Buffer详解 0.什么是protoBuf protoBuf是一种灵活高效的独立于语言平台的结构化数据表示方法,与XML相比,protoBuf更 ...

随机推荐

  1. mybatis实战教程(mybatis in action)之四:实现关联数据的查询

    有了前面几章的基础,对一些简单的应用是可以处理的,但在实际项目中,经常是关联表的查询,比如最常见到的多对一,一对多等.这些查询是如何处理的呢,这一讲就讲这个问题.我们首先创建一个Article 这个表 ...

  2. Elasticsearch mysql 增量同步 三表联合 脚本

    在上一篇中简略的说了一下es同步数据脚本的大致情况,但是实际情况里肯定不会像上一篇里面的脚本那么简单.比如目前我就有三张表,两张实体表,一张关联表.大致实现如下: bin目录建立一个statefile ...

  3. Python六大开源框架对比:Web2py略胜一筹

    Python是一门动态.面向对象语言.其最初就是作为一门面向对象语言设计的,并且在后期又加入了一些更高级的特性.除了语言本身的设计目的之外,Python标准库也是值得大家称赞的,Python甚至还自带 ...

  4. POJ 3468 A Simple Problem with Integers(线段树 成段增减+区间求和)

    A Simple Problem with Integers [题目链接]A Simple Problem with Integers [题目类型]线段树 成段增减+区间求和 &题解: 线段树 ...

  5. eclipse插件

    #eclipse market http://www.eclipse.org/mpc/archive.php http://download.eclipse.org/mpc/mars/ #文件路径 p ...

  6. 用openvswitch配置跨节点的docker网络环境

    在一篇随笔中,我们已经尝试了在不依赖工具的情况下设置docker的ip,连我都想吐槽,MD单机都这么麻烦,在多机的环境中岂不是要了我的小命! 本文就是为了多机环境中各个节点的容器通信而做的,网络拓朴如 ...

  7. Bar菜单

    简介 BarButtonItem:元素(按钮.子菜单.标签.编辑器等) this.barButtonItem3 = new DevExpress.XtraBars.BarButtonItem(); B ...

  8. linux mint 崩溃

    换完linux mint 今天突然崩溃了.出现如下错误 因为是双屏.一个屏幕显示一般,这么不重要.搜了一下,找到解决方案 解决办法 ctrl+atl+f1 login sudo apt-get ins ...

  9. C++11实现生产者消费者问题

    生产者消费者问题是多线程并发中一个非常经典的问题.我在这里实现了一个基于C++11的,单生产者单消费者的版本,供大家参考. #include <windows.h> #include &l ...

  10. VM12.1.1 下载 序列号

    VF5XA-FNDDJ-085GZ-4NXZ9-N20E6UC5MR-8NE16-H81WY-R7QGV-QG2D8ZG1WH-ATY96-H80QP-X7PEX-Y30V4AA3E0-0VDE1-0 ...