在C++项目中调用WebAssembly模块,需要先将目标代码编译为WASM格式,再嵌入对应的WASM运行时完成模块加载和函数调用。整个过程不需要依赖浏览器环境,可在原生桌面程序中实现跨语言能力复用。
编译生成WebAssembly模块
通常我们使用emscripten工具链将C/C++代码编译为WASM模块,首先需要准备一个简单的待调用模块代码。
// 待编译为WASM的模块代码,文件名为add.c
#include <emscripten.h>
// 使用EMSCRIPTEN_KEEPALIVE宏确保函数不会被编译器优化掉
EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
return a + b;
}
执行以下命令编译得到WASM模块文件:
emcc add.c -o add.wasm -s STANDALONE_WASM
编译完成后会生成add.wasm文件,这就是我们需要调用的WebAssembly模块。
选择并嵌入WASM运行时
常见的WASM运行时有Wasmtime、Wasmer等,这里以Wasmtime为例介绍嵌入方法。首先需要安装Wasmtime的C++开发库,在项目编译时链接对应的库文件。
初始化运行时环境
首先需要初始化Wasmtime的引擎、存储和实例等核心对象,代码示例如下:
#include <iostream>
#include <wasmtime.h>
int main() {
// 创建Wasmtime引擎
wasm_engine_t* engine = wasm_engine_new();
if (!engine) {
std::cerr << "创建引擎失败" << std::endl;
return 1;
}
// 创建存储对象
wasm_store_t* store = wasm_store_new(engine);
if (!store) {
std::cerr << "创建存储失败" << std::endl;
wasm_engine_delete(engine);
return 1;
}
// 后续步骤会在这里补充
return 0;
}
加载并实例化WASM模块
接下来需要读取WASM模块的二进制内容,编译为模块并实例化:
#include <fstream>
#include <vector>
#include <iostream>
#include <wasmtime.h>
// 读取WASM文件内容的辅助函数
std::vector<uint8_t> read_wasm_file(const std::string& path) {
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file) {
return {};
}
size_t size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> buffer(size);
file.read(reinterpret_cast<char*>(buffer.data()), size);
return buffer;
}
int main() {
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
// 读取WASM模块文件
std::vector<uint8_t> wasm_data = read_wasm_file("add.wasm");
if (wasm_data.empty()) {
std::cerr << "读取WASM文件失败" << std::endl;
wasm_store_delete(store);
wasm_engine_delete(engine);
return 1;
}
// 编译WASM模块
wasm_module_t* module = wasm_module_new(store, wasm_data.data(), wasm_data.size());
if (!module) {
std::cerr << "编译WASM模块失败" << std::endl;
wasm_store_delete(store);
wasm_engine_delete(engine);
return 1;
}
// 实例化模块
wasm_instance_t* instance = wasm_instance_new(store, module, nullptr, nullptr);
if (!instance) {
std::cerr << "实例化WASM模块失败" << std::endl;
wasm_module_delete(module);
wasm_store_delete(store);
wasm_engine_delete(engine);
return 1;
}
// 后续步骤会在这里补充
return 0;
}
调用WASM模块内的函数
实例化完成后,可以获取模块导出的add函数并传入参数调用:
#include <fstream>
#include <vector>
#include <iostream>
#include <wasmtime.h>
std::vector<uint8_t> read_wasm_file(const std::string& path) {
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file) {
return {};
}
size_t size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> buffer(size);
file.read(reinterpret_cast<char*>(buffer.data()), size);
return buffer;
}
int main() {
wasm_engine_t* engine = wasm_engine_new();
wasm_store_t* store = wasm_store_new(engine);
std::vector<uint8_t> wasm_data = read_wasm_file("add.wasm");
wasm_module_t* module = wasm_module_new(store, wasm_data.data(), wasm_data.size());
wasm_instance_t* instance = wasm_instance_new(store, module, nullptr, nullptr);
// 获取导出的add函数
wasm_extern_vec_t externs;
wasm_instance_exports(instance, &externs);
if (externs.size == 0) {
std::cerr << "未找到导出的函数" << std::endl;
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasm_engine_delete(engine);
return 1;
}
// 假设第一个导出项是add函数
wasm_func_t* add_func = wasm_extern_as_func(externs.data[0]);
if (!add_func) {
std::cerr << "导出项不是函数" << std::endl;
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasm_engine_delete(engine);
return 1;
}
// 准备函数参数,WASM的i32类型对应wasm_val_t
wasm_val_t args[2];
args[0].kind = WASM_I32;
args[0].of.i32 = 10;
args[1].kind = WASM_I32;
args[1].of.i32 = 20;
// 准备接收返回值的数组
wasm_val_t results[1];
results[0].kind = WASM_I32;
// 调用函数
wasm_trap_t* trap = wasm_func_call(add_func, args, results);
if (trap) {
std::cerr << "调用函数时发生错误" << std::endl;
wasm_trap_delete(trap);
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasm_engine_delete(engine);
return 1;
}
// 输出结果
std::cout << "调用add函数结果: " << results[0].of.i32 << std::endl;
// 释放资源
wasm_instance_delete(instance);
wasm_module_delete(module);
wasm_store_delete(store);
wasm_engine_delete(engine);
return 0;
}
注意事项
- 编译WASM模块时需要确保导出的函数没有被优化掉,可添加对应编译宏或者导出声明。
- 不同的WASM运行时API存在差异,实际使用时需要参考对应运行时的官方文档。
- 参数传递时要保证C++侧的类型和WASM模块定义的函数参数类型匹配,避免类型错误导致调用失败。
- 所有WASM运行时的资源使用完成后需要及时释放,避免内存泄漏。
C++WebAssemblyWASM运行时emscripten修改时间:2026-06-22 10:31:00