Google V8 JS调用C++

25 篇文章 0 订阅

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\n", 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++类的假象:

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

JS代码如下:

  1. //script.js  
  2. var cloudapp = new CloudApp(24);  
  3. cloudapp.start();  
  4. var result;  
上面的代码基本可以从函数名称和注释中明白是什么意思。最后再讲一点SetInternalFieldCount:

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


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在C++调用JavaScript,可以使用以下两种方法: 1. Embedding JavaScript engine in C++ 可以使用像V8这样的JavaScript引擎将JavaScript嵌入到C++应用程序中。这样,您可以创建JavaScript对象,调用JavaScript函数,并处理JavaScript事件。 以下是一个使用V8引擎的简单示例: ```c++ #include <iostream> #include <libplatform/libplatform.h> #include <v8.h> int main(int argc, char* argv[]) { v8::V8::InitializeICUDefaultLocation(argv[0]); v8::V8::InitializeExternalStartupData(argv[0]); std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform(); v8::V8::InitializePlatform(platform.get()); v8::V8::Initialize(); v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); { v8::Isolate::Scope isolate_scope(isolate); v8::HandleScope handle_scope(isolate); // Create a new context. v8::Local<v8::Context> context = v8::Context::New(isolate); // Enter the context for compiling and running the hello world script. v8::Context::Scope context_scope(context); // Create a string containing the JavaScript source code. v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, "'Hello' + ', World!'").ToLocalChecked(); // Compile the source code. v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked(); // Run the script to get the result. v8::Local<v8::Value> result = script->Run(context).ToLocalChecked(); // Convert the result to an UTF8 string and print it. v8::String::Utf8Value utf8(isolate, result); std::cout << *utf8 << std::endl; } isolate->Dispose(); v8::V8::Dispose(); v8::V8::ShutdownPlatform(); delete create_params.array_buffer_allocator; return 0; } ``` 2. 使用EMSCRIPTEN编写C++和JavaScript代码 EMSCRIPTEN是一个编译器工具链,它允许将C++代码编译为JavaScript。您可以使用EMSCRIPTEN将C++函数导出到JavaScript中,并在JavaScript中调用它们。 以下是一个使用EMSCRIPTEN的简单示例: ```c++ #include <emscripten.h> extern "C" { int EMSCRIPTEN_KEEPALIVE add(int a, int b) { return a + b; } } ``` 在上面的代码中,`EMSCRIPTEN_KEEPALIVE`指令是必需的,以便EMSCRIPTEN不会将`add`函数从编译后的代码中删除。 然后,您可以在JavaScript中使用以下代码调用该函数: ```javascript import { add } from './add.js'; console.log(add(1, 2)); // 输出 3 ``` 在上面的JavaScript代码中,我们导入了从C++编译的JavaScript模块,并使用导出的`add`函数计算1和2的和。 希望这可以帮助您开始在C++和JavaScript之间进行交互。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值