文章WebAssembly 开发入门 3:使用 C 语言开发介绍了使用 C 语言开发 WebAssembly 模块。如果要将 C 语言中的函数导出到 WebAssembly 模块中,只需在函数定义之前使用 EMSCRIPTEN_KEEPALIVE 宏进行修饰即可自动导出。
WebAssembly 模块中导出的 C 函数具有很大的局限性,参数类型仅支持四种基本数值类型:i32 (32位整数/指针)、i64 (64位整数)、f32 (32位浮点)、f64 (64位浮点)。复杂的结构体数据类型参数只能通过指针类型来代替,并且在 JavaScript 端需要手动计算结构体成员相对于结构体指针的偏移量,代码可维护性差。
为了解决这个问题,Emscripten 提供了 embind(参考:https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html)用于导出 C++ 类。
考虑如下的 C++ 类 Rectangle:
// rectangle.hpp
class Rectangle {
public:
Rectangle(double width, double height);
~Rectangle();
double getArea() const;
double getPerimeter() const;
private:
double width;
double height;
}; Rectangle 类的具体的具体实现:
// rectangle.cpp
#include "rectangle.hpp"
Rectangle::Rectangle(double width, double height)
: width(width), height(height) {}
Rectangle::~Rectangle() = default;
double Rectangle::getArea() const { return width * height; }
double Rectangle::getPerimeter() const { return 2 * (width + height); } 用于导出 Rectangle 类的 Emscripten 胶水代码为:
// module.cpp
#include "emscripten/emscripten.h"
#include "emscripten/bind.h"
#include "rectangle.hpp"
using namespace emscripten;
EMSCRIPTEN_BINDINGS(statmodule) {
class_<Rectangle>("Rectangle")
.constructor<double, double>()
.function("getArea", &Rectangle::getArea)
.function("getPerimeter", &Rectangle::getPerimeter);
} 头文件 emscripten/bind.h 是 embind 的头文件。宏 EMSCRIPTEN_BINDINGS 用于定义 C++ 类到 WebAssembly 导出类的绑定,emscripten::class_<...> 定义了要导出的类,.constructor 定义了导出的类构造函数,.function 定义了导出的类方法。
CMakeLists.txt 中的链接参数也需要加入 -l embind,具体为:
# 设置 emcc 编译参数
# -l embind 链接 embind
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-O3 \
-s EXPORT_NAME='Module' \
-l embind \
-s MODULARIZE=1") CMakeLists.txt 的详细配置参考WebAssembly 开发入门 4:使用 CMake 编译模块。项目编译之后会生成 .wasm 和 .js 模块文件,导出 web 页面之后就可以使用了。
下面的示例代码演示了在 JavaScript 中使用导出的 Rectangle 类:
async function main() {
const module = await Module();
const rectangle = new module.Rectangle(10, 20);
console.log(rectangle.getPerimeter());
console.log(rectangle.getArea());
}
main(); 相比于导出 C 函数的方式,使用 embind 会增加 WebAssembly 二进制文件的大小,也会引入少许类型包装和转换的额外运行成本,同时也大大简化了在 JavaScript 和 WebAssembly 中使用 C++ 类型的难度。
#WebAssembly# #Wasm# #embind# #Emscripten# #CPP# #CMake# #类#
(完)