Emscripten教程之C++和JavaScript绑定(三)
翻译:云荒杯倾
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绑定(三)相关推荐
- Emscripten教程之emcc编译命令
https://segmentfault.com/a/1190000011335568 emcc(Emscripten Compiler Frontend)介绍 翻译:云荒杯倾 本文是Emscript ...
- python winform开发_c#教程之.Net WInform开发笔记(三)谈谈自制控件(自定
末日这天写篇博客吧,既然没来,那就纪念一下. 这次谈谈自制控件,也就是自定义控件,先上图,再说 1.扩展OpenFileDialog,在OpenFileDialog中添加各种文件(.txt,.jpg, ...
- Excel VLOOKUP实用教程之 04 vlookup如何实现三变量查找,三个条件字段查询数据?(教程含数据excel)
实战需求 vlookup如何实现三变量查找,三个条件字段查询数据? 文章目录 <示例 1 – 查找 Brad 的数学分数> <示例 2 – 双向查找> <示例 3 – 使 ...
- iOS Sprite Kit教程之xcode安装以及苹果帐号绑定
iOS Sprite Kit教程之xcode安装以及苹果帐号绑定 其它的网站上下载安装Xcode 有时候,应用商店下载较慢,所以用户也可以选择从其他网站下载Xcode安装文件.下面讲解这种Xcode的 ...
- javascript技术教程蔡敏_程序员都必掌握的前端教程之JavaScript基础教程(上)
阅读本文约需要10分钟,您可以先关注我们,避免下次无法找到. 本篇文章成哥继续带大家来学习前端教程之JavaScript,网页的动态事件基本上都是靠它来实现的.下面我们就一起来学习内容吧! 01 Ja ...
- 好程序员前端教程之JavaScript闭包和匿名函数的关系详解...
好程序员前端教程之JavaScript闭包和匿名函数的关系详解 本文讲的是关于JavaScript闭包和匿名函数两者之间的关系,从匿名函数概念到立即执行函数,最后到闭包.下面一起来看看文章分析,希望你 ...
- html语言面向对象,自学html5教程之JavaScript面向对象
原标题:自学html5教程之JavaScript面向对象 1.对象是什么 面向对象编程(Object Oriented Programming,缩写为 OOP)是目前主流的编程范式. 它将真实世界各种 ...
- miniui设置属性值_jQuery教程之MiniUi绑定mini-combobox下拉框
本篇文章探讨了jQuery教程之MiniUi绑定mini-combobox下拉框,希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入. < 一:最先开始使用json字符串绑定co ...
- 深圳Web培训:web应用安全培训教程之react
深圳Web培训:web应用安全培训教程之react react是一个用于构建用户界面的JavaScript库文件,react可以使构建交互UI的过程变得简单.对于你的应用里面的每个状态视图,当数据变化 ...
最新文章
- 成为多编程语言人才的诀窍你想知道吗
- 探寻背后的机制化繁为简:网站程序升级不过是文件同步
- Silverlight Telerik控件学习:带CheckBox复选框的树形TreeView控件
- android英文文献翻译,有关android技术英文文献翻译
- excel教程自学网_Excel自学教程:万能查找函数Lookup的神应用和技巧
- ML《决策树(三)CART》
- limit where group by having select
- POJ2031Building a Space Station
- 最新微软Windows Server 2008之一 系统安装
- JS Learun 消息对话框
- 如何清空c盘只剩系统_怎么清空c盘只保留系统文件,详情介绍
- 树莓派制作内网dns服务器,树莓派使用dnsmasq搭建DNS服务器
- LTspice使用第三方spice模型进行仿真
- android应用商店代码,仿小米应用商店Android客户端
- 2022年国家高新技术企业认定最新变化
- 设计模式-备忘录模式(快照模式)
- 谷歌浏览器扩展工具---eye dropper取色器使用
- 考研名校压分黑名单——斯基
- PhpStorm-2017.3
- USACO 2022 January Contest, Bronze
热门文章
- 使用Shell和Java驱动程序的MongoDB身份验证配置示例
- 开课吧:深入了解软件开发原则有哪些?
- 深入了解C++与C语言的区别
- C语言基础教程之如何定义变量
- springboot中通过cors协议解决跨域问题
- 计算指定人数班级的班级平均成绩(计数器控制控制的循环)
- pacemaker+mysql+drbd
- markdown这么好用的东西我才知道。。。多么不折腾的我。。。
- 拓扑排序 Codeforces Round #290 (Div. 2) C. Fox And Names
- Leecode 953. Verifying an Alien Dictionary[Easy]