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三步:
- #include "v8.h"
- #include <string.h>
- #include <stdio.h>
- using namespace v8;
- using namespace std;
- Handle<Value> Yell(const Arguments& args) {
- HandleScope handle_scope;
- char buffer[4096];
- memset(buffer, 0, sizeof(buffer));
- Handle<String> str = args[0]->ToString();
- str->WriteAscii(buffer);
- printf("Yell: %s\n", buffer);
- return Undefined();
- }
- int main(int argc, char** argv) {
- HandleScope handle_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::Scope context_scope(cxt);
- Handle<String> source = String::New("yell('Google V8!')");
- Handle<Script> script = Script::Compile(source);
- Handle<Value> result = script->Run();
- cxt.Dispose();
- }
第一步,定义一个FunctionTempte并与C++函数绑定:
- Handle<FunctionTemplate> fun = FunctionTemplate::New(Yell);
第二部,定义一个ObectTemplate,并向该对象注册一个FunctionTemplate
- Handle<ObjectTemplate> global = ObjectTemplate::New();
- global->Set(String::New("yell"), fun);
第三部,将该对象注册到JS的global中去:
- Persistent<Context> cxt = Context::New(NULL, global);
JS调用C++类
JS其实是无法直接使用C++类的,当JS中new一个对象的时候,需要手动将C++产生的对象同JS的对象进行绑定。从而就造成了JS使用C++类的假象:
- var cloudapp = new CloudApp();
- 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中:
- //....
- void* ptr = ...
- object->SetInternalField(0, External::New(ptr));
上面这段代码将一个C++指针ptr保存在InternalField的index 0处。然后将来的某个时候如果需要获取这个指针,只需使用index 0来获取该指针。
将C++指针从InternalField中获取出来:
- 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 实现如下:
- Local<Value> External::Wrap(void* value) {
- return External::New(value);
- }
- void* External::Unwrap(Handle<v8::Value> obj) {
- return External::Cast(*obj)->Value();
- }
External其实就是C++指针的载体。这也就解释了前面在InternalField中设置和获取InternalField中的C++指针的时候,使用了External::New和wrap->Value()的原因了。External::Value()返回的就是C++指针。
下面开始上代码,看看究竟是如何实现JS调用C++类的:
- //C++Externtion
- #include "v8.h"
- #include "utils.h"
- #include <iostream>
- #include <string>
- using namespace std;
- using namespace v8;
- enum AppState{
- IDEL = 0,
- LOADED,
- STOP
- };
- class CloudApp {
- public:
- CloudApp(int id) {
- state = IDEL;
- appId = id;
- }
- void start() {
- cout << "CloudApp been Loaded id = " << appId << endl;
- state = LOADED;
- };
- int getState() { return state;}
- int getAppId() { return appId;}
- private:
- AppState state;
- int appId;
- };
- //向MakeWeak注册的callback.
- void CloudAppWeakReferenceCallback(Persistent<Value> object
- , void * param) {
- if (CloudApp* cloudapp = static_cast<CloudApp*>(param)) {
- delete cloudapp;
- }
- }
- //将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的主要目的是为了检测Persistent Handle除了当前Persistent
- //的唯一引用外,没有其他的引用,就可以析构这个Persistent Handle了,
- //同时调用MakeWeak的callback。这是我们可以再这个callback中delete
- //C++指针
- persistentCloudApp.MakeWeak(parameter, CloudAppWeakReferenceCallback);
- return persistentCloudApp;
- }
- //将JS传进来的参数解析之后,创建C++对象
- CloudApp* NewCloudApp(const Arguments& args) {
- CloudApp* cloudApp = NULL;
- if (args.Length() == 1) {
- cloudApp = new CloudApp(args[0]->ToInt32()->Value());
- } else {
- v8::ThrowException(String::New("Too many parameters for NewCloudApp"));
- }
- return cloudApp;
- }
- //相当于JS对应的构造函数,当JS中使用new CloudApp的时候,这个callback将自动被调用
- Handle<Value> CloudAppConstructCallback(const Arguments& args) {
- if (!args.IsConstructCall())
- return Undefined();
- CloudApp* cloudapp = NewCloudApp(args);
- Handle<Object> object = args.This();
- object->SetInternalField(0, MakeWeakCloudApp(cloudapp));
- return Undefined();
- }
- Handle<Value> GetState(const Arguments& 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);
- return Integer::New(cloudapp->getState());
- }
- Handle<Value> GetAppId(const Arguments& 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);
- return Integer::New(cloudapp->getAppId());
- }
- Handle<Value> Start(const Arguments& 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();
- return Undefined();
- }
- void SetupCloudAppInterface(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);
- }
- void InitialnilizeInterface(Handle<ObjectTemplate> global) {
- SetupCloudAppInterface(global);
- }
- void LoadJsAndRun() {
- Handle<String> source = ReadJS("script.js");
- Handle<Script> script = Script::Compile(source);
- Handle<Value> result = script->Run();
- printValue(result);
- }
- void Regist2JsContext(Handle<ObjectTemplate>& object
- , Persistent<Context>& context) {
- context = Context::New(NULL, object);
- }
- int main(int argc, char** argv) {
- HandleScope handle_scope;
- Handle<ObjectTemplate> global = ObjectTemplate::New();
- Persistent<Context> context;
- InitialnilizeInterface(global);
- Regist2JsContext(global, context);
- Context::Scope context_scope(context);
- LoadJsAndRun();
- context.Dispose();
- return 0;
- }
JS代码如下:
- //script.js
- var cloudapp = new CloudApp(24);
- cloudapp.start();
- var result;
上面的代码基本可以从函数名称和注释中明白是什么意思。最后再讲一点SetInternalFieldCount:
- 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++的更多相关文章
- Google V8编程详解附录
Google V8编程详工具函数 头文件:utils.h #ifndef UTILS_H_ #define UTILS_H_ #include "v8.h" #include &l ...
- Google V8编程详解(序)Cloud App
此系列文章转载于此http://blog.csdn.net/feiyinzilgd/article/details/8247723 应用程序发展到今天,应用程序的概念也在不断地发生着 ...
- Google V8编程详解(四)Context
http://blog.csdn.net/feiyinzilgd/article/details/8266780 上一章,比较略提了下V8的Context.本章将详细的讲解下Context的概念以及用 ...
- Google V8编程详解(三)Handle & HandleScope
上一章简单的演示了一个Helloworld Demo.里面涉及到了V8的一些基本类型和概念,本章将围绕这个Demo对V8的基本类型和相关概念进行讲解. 这里还是先把Demo贴出来便于后面分析: #in ...
- Google V8编程详解(二)HelloWorld
转自http://blog.csdn.net/feiyinzilgd/article/details/8248448 上一章讲到了V8的编译和安装,这一章开始从一个demo着手. 这里选用了官方文档的 ...
- Google V8编程详解(一)V8的编译安装(Ubuntu)
V8的编译比较简单,需要同时安装git和svn. 下载V8源码: git clone git://github.com/v8/v8.git v8 && cd v8 切换到最新版本: g ...
- Linux 网络编程详解五(TCP/IP协议粘包解决方案二)
ssize_t recv(int s, void *buf, size_t len, int flags); --与read相比,只能用于网络套接字文件描述符 --当flags参数的值设置为MSG_P ...
- ORACLE PL/SQL编程详解
ORACLE PL/SQL编程详解 编程详解 SQL语言只是访问.操作数据库的语言,并不是一种具有流程控制的程序设计语言,而只有程序设计语言才能用于应用软件的开发.PL /SQL是一种高级数据库程序设 ...
- 前端后台以及游戏中使用Google Protocol Buffer详解
前端后台以及游戏中使用Google Protocol Buffer详解 0.什么是protoBuf protoBuf是一种灵活高效的独立于语言平台的结构化数据表示方法,与XML相比,protoBuf更 ...
随机推荐
- javascript判断某种元素是否进入可视区域
判断是否在指定的可视区域内,先用最简单的方式,比如整个页面为可视区域 找到几个关键因素: sTop= $(window).scrollTop(); //滚动条距顶部的高度 clientHeight= ...
- oracle之check约束小结
一下是Ocp考试指导中,对于oracle约束的描述: The constraint types supported by the Oracle database are as follows:UNIQ ...
- MVC4 +EasyUI 使用TreeGrid 方法
用easyui已经有2年了,换了新环境,要求用mvc开发,所以想把原来的项目直接用mvc重构. 在使用TreeGird的时候出现了问题,发现在转换为treegrid的json 很费劲,一直都是用的ea ...
- JVM实用参数(六) 吞吐量收集器
在实践中我们发现对于大多数的应用领域,评估一个垃圾收集(GC)算法如何根据如下两个标准: 吞吐量越高算法越好 暂停时间越短算法越好 首先让我们来明确垃圾收集(GC)中的两个术语:吞吐量(through ...
- jquery遍历数组与筛选数组的方法
grepgrep()方法用于数组元素过滤筛选 grep(array,callback,invert)array:待过滤数组;callback:处理数组中的每个元素,并过滤元素,该函数中包含两个参数,第 ...
- 关于Android 打开新的Activity 虚拟键盘的弹出与不弹出
关于Android 打开新的Activity 虚拟键盘的弹出与不弹出 打开Activity 时 在相应的情况 弹出虚拟键盘 或者 隐藏虚拟键盘 会给用户非常好的用户体验 , 实现起来也比较简单 只需 ...
- 基于WebDriver&TestNG 实现自己的Annotation @TakeScreenshotOnFailure
相信用过Selenium WebDriver 的朋友都应该知道如何使用WebDriver API实现Take Screenshot的功能. 在这篇文章里,我主要来介绍对failed tests实现 t ...
- js 取得 Unix时间戳(Unix timestamp)
js 取得 Unix时间戳 Unix时间戳(Unix timestamp),或称Unix时间(Unix time).POSIX时间(POSIX time),是一种时间表示方式,定义为从格林威治时间19 ...
- tomcat6配置jndi连接数据库的方式
eworkflow工作流+eform表单+ebiao报表集成在一起,用tomcat6发布,并用jndi连接数据库,数据库是sqlserver2005,配置如下: 1.在tomcat6\conf\con ...
- js代码生成form,解决mvc的url参数过长问题
在MVC项目中,通常下载的文件的简单方式是直接采用 location.href+查询参数方式. var searchParams = { studentName: $("#StudentNa ...