wasm 编译使用示例
WebAssembly 是一种新的编码方式,可以在现代的 Web 浏览器中运行——它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C/C++、C# 和 Rust 等语言提供编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。
以下示例,将展示如何使用 emscripten 编译 C++ 代码为 wasm 模块,并在 web 页面中调用。
一 安装 Emscripten
官方文档:https://emscripten.org/docs/getting_started/downloads.html#installation-instructions-using-the-emsdk-recommended
以下是详细安装步骤。
- 克隆 emsdk 仓库:
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
- 安装 SDK:
./emsdk.bat install latest
./emsdk.bat activate latest
- 配置环境变量:
source emsdk_env.sh
# windows
./emsdk_env.bat
在环境变量中定义 EMSDK、EMSDK_EMCC、EMSDK_NODE、EMSDK_PYTHON,并将其加入到环境变量中。
- 验证安装:
emcc --version
二 编译 wasm
1 C++ 代码示例
假如我们有一个 C++ 编写的数学库,需要暴露其中的 add 方法给 JavaScript 使用。其目录结构如下:
math/
├── math.h
├── math.cpp
头文件 math.h
和 源文件 math.cpp
内容如下:
// math.h
#ifndef MATH_H
#define MATH_Hint add(int a, int b);#endif
// math.cpp
int add(int a, int b) {return a + b;
}
2 编写绑定文件
与 math
目录同级,新建一个 bind
目录,用于存放绑定文件,然后在其中创建 math_bind.cpp
,内容如下:
#include <emscripten/bind.h>
#include "math.h"using namespace emscripten;EMSCRIPTEN_BINDINGS(math_module) {function("add", &add);
}
在绑定文件中,我们使用 function
函数将 C++ 函数 add
暴露给 JavaScript 使用。
3 编译命令
在 bind
目录下执行以下命令:
em++ ../math/math.cpp ./math_bind.cpp -o ./build/math.js -s WASM=1 -s NO_EXIT_RUNTIME=1 --bind
上述命令中:
em++
:emscripten 编译命令。../math/math.cpp
:C++ 源文件路径。./math_bind.cpp
:绑定文件路径。-o ./build/math.js
:输出文件路径。-s WASM=1
:生成 wasm 文件。-s NO_EXIT_RUNTIME=1
:防止退出 runtime。--bind
:生成绑定文件。
执行命令后,会在 bind
目录下的 build
目录中生成 math.js
和 math.wasm
文件。
还有一些其他参数,可以将导出的 js 文件模块化,使之成为一个 es 模块,相关编译指令:
-s MODULARIZE=1
:使生成的代码可以作为模块使用。-s EXPORT_NAME='createMathWasmModule'
:指定模块的名称。
至此,C++ 代码已经编译导出 wasm 就完成了,在需要使用的地方引入 math.js
和 math.wasm
文件即可。接下来我们编写 HTML 页面调用 wasm 模块。
三 测试使用 wasm
与 math
目录同级,新建一个 web
目录,用于存放网站相关文件。将 math.js
和 math.wasm
文件拷贝到 web
目录中。在 web
目录下创建 index.html
,内容如下:
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>wasm 测试</title>
</head>
<body><h2>WASM 加法测试</h2><div><input type="number" id="num1" value="0"> +<input type="number" id="num2" value="0"> =<span id="result"></span></div><button onclick="calculate()">计算</button><script src="math.js"></script><script>// 等待WASM模块加载完成Module.onRuntimeInitialized = function () {console.log('WASM 模块已加载');}function calculate() {if (!Module) {console.error('WASM 未加载');return;}const num1 = parseInt(document.getElementById('num1').value);const num2 = parseInt(document.getElementById('num2').value);// 调用WASM中的add函数const sum = Module.add(num1, num2);document.getElementById('result').textContent = sum;}</script>
</body>
</html>
最终项目结构如下:
project/
├── bind/
│ ├── math_bind.cpp
│ └── build/
│ ├── math.js
│ └── math.wasm
├── math/
│ ├── math.h
│ └── math.cpp
├── web/
│ ├── index.html
│ └── math.js
│ └── math.wasm
若使用的模块好的,则可以通过 createMathWasmModule
函数获取 wasm 模块,然后调用其中的方法。
import createMathWasmModule from './math.js';const wasmModule = await createMathWasmModule();
console.log(wasmModule.add(1, 2));