翻译:云荒杯倾

Embind用于绑定C++函数和类到JavaScript,这样编译代码就能在js中以一种很自然的方式来使用。Embind也支持从C++调JavaScript的class。

Embind支持绑定大多数C++的结构,包括C++11和C++14中引入的。它只有一个明显的限制就是目前还不支持raw pointers with complicated lifetime semantics。

本文展示了如何使用EMSCRIPTEN_BINDINGS()块来创建函数、类、值类型、指针(包括原始和智能指针)、枚举和常量的绑定,以及如何为抽象类创建绑定,这些抽象类可以在JavaScript中被重写。它还简要介绍了如何管理传递给JavaScript的c++对象句柄的内存。

note:
Embind的灵感来自 Boost.Python,他们使用非常相似的方法定义绑定。

一个简单例子

下面的代码使用EMSCRIPTEN_BINDINGS()暴露了C++ lerp()函数给JavaScript。

    // quick_example.cpp#include <emscripten/bind.h>using namespace emscripten;float lerp(float a, float b, float t) {return (1 - t) * a + t * b;}EMSCRIPTEN_BINDINGS(my_module) {function("lerp", &lerp);}

为了使用embind编译上例,请调用emcc的bing选项:

emcc --bind -o quick_example.js quick_example.cpp

生成的quick_example.js文件可以作为node模块加载,也可以使用<script>加载:

     <!doctype html><html><script src="quick_example.js"></script><script>console.log('lerp result: ' + Module.lerp(1, 2, 0.5));</script></html>

当quick_example.js文件初始化加载后, EMSCRIPTEN_BINDINGS()中代码会运行。

所有通过Embind暴露的symblols都可以在Module对象获取。

暴露一个类给JavaScript需要比较复杂的绑定语句,比如:

    class MyClass {public:MyClass(int x, std::string y): x(x), y(y){}void incrementX() {++x;}int getX() const { return x; }void setX(int x_) { x = x_; }static std::string getStringFromInstance(const MyClass& instance) {return instance.y;}private:int x;std::string y;};// Binding codeEMSCRIPTEN_BINDINGS(my_class_example) {class_<MyClass>("MyClass").constructor<int, std::string>().function("incrementX", &MyClass::incrementX).property("x", &MyClass::getX, &MyClass::setX).class_function("getStringFromInstance", &MyClass::getStringFromInstance);}

绑定块在一个临时class_对象上定义了成员函数调用链(Boost.Python也是同样风格)。

note:
你应该只绑定那些你实际需要的项(将它作为一个规则或原则),因为每个绑定会增加代码大小。比如,内部方法和私有变量可以很少绑定。

在JavaScript中定义和使用MyClass实例的代码如下:

    var instance = new Module.MyClass(10, "hello");instance.incrementX();instance.x; // 12instance.x = 20; // 20Module.MyClass.getStringFromInstance(instance); // "hello"instance.delete();

内存管理

因为JavaScript,尤其是ECMA-262 Edition 5.1,不支持 finalizers or weak references with callbacks,因此Emscripten没有办法调用C++对象的析构函数。

警告:
JavaScript代码必须明确删除C++对象的句柄,否则Emscripten堆会无限增长。
    var x = new Module.MyClass;x.method();x.delete();var y = Module.myFunctionThatReturnsClassInstance();y.method();y.delete();

值类型

对基本类型进行手动内存管理是麻烦的,所以embind对值类型提供了支持。包括Value arrays和 value objects,分别对应js的array和object。

示例:

    struct Point2f {float x;float y;};struct PersonRecord {std::string name;int age;};PersonRecord findPersonAtLocation(Point2f);EMSCRIPTEN_BINDINGS(my_value_example) {value_array<Point2f>("Point2f").element(&Point2f::x).element(&Point2f::y);value_object<PersonRecord>("PersonRecord").field("name", &PersonRecord::name).field("age", &PersonRecord::age);function("findPersonAtLocation", &findPersonAtLocation);}

以下代码就不需要担心手动生命周期管理。

    var person = Module.findPersonAtLocation([10.2, 156.5]);console.log('Found someone! Their name is ' + person.name + ' and they are ' + person.age + ' years old');

高级类概念(todo)

重载函数

构造函数和函数可以根据参数数量重载,但embind不支持根据参数类型重载。当你指定一个重载,请使用select_overload()帮助函数选中合适的签名。

    struct HasOverloadedMethods {void foo();void foo(int i);void foo(float f) const;};EMSCRIPTEN_BINDING(overloads) {class_<HasOverloadedMethods>("HasOverloadedMethods").function("foo", select_overload<void()>(&HasOverloadedMethods::foo)).function("foo_int", select_overload<void(int)>(&HasOverloadedMethods::foo)).function("foo_float", select_overload<void(float)const>(&HasOverloadedMethods::foo));}

枚举

embind支持C++98枚举和C++11枚举类。

    enum OldStyle {OLD_STYLE_ONE,OLD_STYLE_TWO};enum class NewStyle {ONE,TWO};EMSCRIPTEN_BINDINGS(my_enum_example) {enum_<OldStyle>("OldStyle").value("ONE", OLD_STYLE_ONE).value("TWO", OLD_STYLE_TWO);enum_<NewStyle>("NewStyle").value("ONE", NewStyle::ONE).value("TWO", NewStyle::TWO);}

JavaScript调用方式如下:

    Module.OldStyle.ONE;Module.NewStyle.TWO;

常量

向JavaScript暴露一个常量:

    EMSCRIPTEN_BINDINGS(my_constant_example) {constant("SOME_CONSTANT", SOME_CONSTANT);}

内存视图

在某些情况下,将原始二进制数据以一个类型化数组的形式直接暴露给JavaScript代码是有价值的。这对于直接从堆上上传大型WebGL纹理非常有用。

内存视图应该像指针一样对待;生命周期和有效性不由运行时管理的,如果底层对象被修改或重新分配,则很容易损坏数据。

    #include <emscripten/bind.h>#include <emscripten/val.h>using namespace emscripten;unsigned char *byteBuffer = /* ... */;size_t bufferLength = /* ... */;val getBytes() {return val(typed_memory_view(bufferLength, byteBuffer));}EMSCRIPTEN_BINDINGS(memory_view_example) {function("getBytes", &getBytes);}

下面JavaScript代码接收类型数组视图

    var myUint8Array = Module.getBytes()var xhr = new XMLHttpRequest();xhr.open('POST', /* ... */);xhr.send(myUint8Array);

使用val将JavaScript翻译为C++

Embind提供了一个c++类,emscripten::val,您可以使用它将JavaScript代码转换为c++。使用val,可以在c++中调用JavaScript对象,读取和写入它们的属性,或者强制它们成为c++值,比如bool、int或std::string。

下面代码展示了你可以通过val在C++中调用JavaScript的 Web Audio API。
首先看一下js的代码,展示js怎么用这个API:

    // Get web audio api contextvar AudioContext = window.AudioContext || window.webkitAudioContext;// Got an AudioContext: Create context and OscillatorNodevar context = new AudioContext();var oscillator = context.createOscillator();// Configuring oscillator: set OscillatorNode type and frequencyoscillator.type = 'triangle';oscillator.frequency.value = 261.63; // value in hertz - middle C// Playingoscillator.connect(context.destination);oscillator.start();// All done!

然后使用val将代码翻译成c++,如下:

    #include <emscripten/val.h>#include <stdio.h>#include <math.h>using namespace emscripten;int main() {val AudioContext = val::global("AudioContext");if (!AudioContext.as<bool>()) {printf("No global AudioContext, trying webkitAudioContext\n");AudioContext = val::global("webkitAudioContext");}printf("Got an AudioContext\n");val context = AudioContext.new_();val oscillator = context.call<val>("createOscillator");printf("Configuring oscillator\n");oscillator.set("type", val("triangle"));oscillator["frequency"].set("value", val(261.63)); // Middle Cprintf("Playing\n");oscillator.call<void>("connect", context["destination"]);oscillator.call<void>("start", 0);printf("All done!\n");}

首先使用global()取全局AudioContext对象(如果不存在就取webkitAudioContext对象),然后使用new_()创建实例,从实例我们可以创建oscillator,设置set()它的属性,然后播放。

内建类型转换

embind为许多标准C++类型提供类型转换

C++类型 JavaScript类型
void undefined
bool true or false
char number
signed char number
unsigned char number
short number
ungigned short number
int number
unsigned int number
log number
unsigned long number
float number
double number
std::string ArrayBuffer, Uint8Array, Uint8ClampedArray, Int8Array, or String
std::wstring String (UTF-16 code units)
emscripten::val anything

为了方便,embind还提供了工厂函数用来注册std::vector<T>(register_vector())和std::map<K, V>(register_map())类型。

    EMSCRIPTEN_BINDINGS(stl_wrappers) {register_vector<int>("VectorInt");register_map<int,int>("MapIntInt");}

性能

在撰写本文时,还没有对标准基准测试或相对于WebIDL Binder的全面的embind性能测试。

简单函数的调用开销在200ns左右。虽然还有进一步优化的空间,但到目前为止,它在实际应用程序中的性能已经被证明是完全可以接受的。


Emscripten代码移植系列文章

Emscripten代码移植主题系列文章是emscripten中文站点的一部分内容。
本文是第三个主题第二篇文章。
第一个主题介绍代码可移植性与限制
第二个主题介绍Emscripten的运行时环境
第三个主题第一篇文章介绍连接C++和JavaScript
第三个主题第二篇文章介绍embind
第四个主题介绍文件和文件系统

Emscripten教程之C++和JavaScript绑定(三)相关推荐

  1. Emscripten教程之emcc编译命令

    https://segmentfault.com/a/1190000011335568 emcc(Emscripten Compiler Frontend)介绍 翻译:云荒杯倾 本文是Emscript ...

  2. python winform开发_c#教程之.Net WInform开发笔记(三)谈谈自制控件(自定

    末日这天写篇博客吧,既然没来,那就纪念一下. 这次谈谈自制控件,也就是自定义控件,先上图,再说 1.扩展OpenFileDialog,在OpenFileDialog中添加各种文件(.txt,.jpg, ...

  3. Excel VLOOKUP实用教程之 04 vlookup如何实现三变量查找,三个条件字段查询数据?(教程含数据excel)

    实战需求 vlookup如何实现三变量查找,三个条件字段查询数据? 文章目录 <示例 1 – 查找 Brad 的数学分数> <示例 2 – 双向查找> <示例 3 – 使 ...

  4. iOS Sprite Kit教程之xcode安装以及苹果帐号绑定

    iOS Sprite Kit教程之xcode安装以及苹果帐号绑定 其它的网站上下载安装Xcode 有时候,应用商店下载较慢,所以用户也可以选择从其他网站下载Xcode安装文件.下面讲解这种Xcode的 ...

  5. javascript技术教程蔡敏_程序员都必掌握的前端教程之JavaScript基础教程(上)

    阅读本文约需要10分钟,您可以先关注我们,避免下次无法找到. 本篇文章成哥继续带大家来学习前端教程之JavaScript,网页的动态事件基本上都是靠它来实现的.下面我们就一起来学习内容吧! 01 Ja ...

  6. 好程序员前端教程之JavaScript闭包和匿名函数的关系详解...

    好程序员前端教程之JavaScript闭包和匿名函数的关系详解 本文讲的是关于JavaScript闭包和匿名函数两者之间的关系,从匿名函数概念到立即执行函数,最后到闭包.下面一起来看看文章分析,希望你 ...

  7. html语言面向对象,自学html5教程之JavaScript面向对象

    原标题:自学html5教程之JavaScript面向对象 1.对象是什么 面向对象编程(Object Oriented Programming,缩写为 OOP)是目前主流的编程范式. 它将真实世界各种 ...

  8. miniui设置属性值_jQuery教程之MiniUi绑定mini-combobox下拉框

    本篇文章探讨了jQuery教程之MiniUi绑定mini-combobox下拉框,希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入. < 一:最先开始使用json字符串绑定co ...

  9. 深圳Web培训:web应用安全培训教程之react

    深圳Web培训:web应用安全培训教程之react react是一个用于构建用户界面的JavaScript库文件,react可以使构建交互UI的过程变得简单.对于你的应用里面的每个状态视图,当数据变化 ...

最新文章

  1. 成为多编程语言人才的诀窍你想知道吗
  2. 探寻背后的机制化繁为简:网站程序升级不过是文件同步
  3. Silverlight Telerik控件学习:带CheckBox复选框的树形TreeView控件
  4. android英文文献翻译,有关android技术英文文献翻译
  5. excel教程自学网_Excel自学教程:万能查找函数Lookup的神应用和技巧
  6. ML《决策树(三)CART》
  7. limit where group by having select
  8. POJ2031Building a Space Station
  9. 最新微软Windows Server 2008之一 系统安装
  10. JS Learun 消息对话框
  11. 如何清空c盘只剩系统_怎么清空c盘只保留系统文件,详情介绍
  12. 树莓派制作内网dns服务器,树莓派使用dnsmasq搭建DNS服务器
  13. LTspice使用第三方spice模型进行仿真
  14. android应用商店代码,仿小米应用商店Android客户端
  15. 2022年国家高新技术企业认定最新变化
  16. 设计模式-备忘录模式(快照模式)
  17. 谷歌浏览器扩展工具---eye dropper取色器使用
  18. 考研名校压分黑名单——斯基
  19. PhpStorm-2017.3
  20. USACO 2022 January Contest, Bronze

热门文章

  1. 使用Shell和Java驱动程序的MongoDB身份验证配置示例
  2. 开课吧:深入了解软件开发原则有哪些?
  3. 深入了解C++与C语言的区别
  4. C语言基础教程之如何定义变量
  5. springboot中通过cors协议解决跨域问题
  6. 计算指定人数班级的班级平均成绩(计数器控制控制的循环)
  7. pacemaker+mysql+drbd
  8. markdown这么好用的东西我才知道。。。多么不折腾的我。。。
  9. 拓扑排序 Codeforces Round #290 (Div. 2) C. Fox And Names
  10. Leecode 953. Verifying an Alien Dictionary[Easy]