Emscripten 从 JavaScript 调用 C/C++

30 阅读2分钟

Emscripten是一个开源编译器工具链,主要用于将C/C++代码编译为WebAssembly(Wasm)

因为最后生成的WASM是在前端使用和运行,所以少不了c/c++和js代码之间的接口调用。

Emscripten 提供了三种从 C/C++ 调用 JavaScript 的主要方法:

  1. 使用ccall直接调用
  2. 使用cwrap包装成JS函数
  3. 通过Module对象以下划线_开头的函数名直接调用(这个最方便使用)

1. 编写供 JS 调用的 C++ 代码(基本语法和宏)

#include <emscripten.h> // 必须包含的头文件

// 使用 extern "C" 避免 C++ 名称修饰
extern "C" {

    // 使用 EMSCRIPTEN_KEEPALIVE 宏确保函数不会被优化删除
    EMSCRIPTEN_KEEPALIVE
    int add(int a, int b) {
        return a + b;
    }

} // extern "C"

2. JS 调用 c++ 代码

2.1 ccall

// 使用 ccall(函数名,返回类型,[参数类型,...],[参数值,...])
var result = Module.ccall('add', 'number', ['number', 'number'], [10, 20]); 
console.log(result); // 输出 30

2.2 cwrap

// 使用 cwrap(函数名,返回类型,[参数类型,...])
var addFunction = Module.cwrap('add', 'number', ['number', 'number']);
var result = addFunction(10, 20);
console.log(result); // 输出 30

优点:自动化程度高,C函数参数类型为char*时,Emscripten会自动分配和释放临时内存

2.3 通过Module对象以下划线_开头的函数名直接调用

var result = Module._add(10, 20);
console.log(result); // 输出 30

减少调用开销,性能更好,需手动管理内存

3. 如何处理复杂数据类型

对于字符串、数组等复杂数据类型,你需要通过Emscripten的堆(Heap)来进行内存操作。

3.1 字符串传递:使用Emscripten提供的字符串转换函数

C++ 返回字符串

// C++ 返回字符串
EMSCRIPTEN_KEEPALIVE
const char* get_greeting() {
    return "Hello from C++!";
}
// JavaScript 端调用并转换字符串
var ptr = Module._get_greeting();
var str = Module.UTF8ToString(ptr);
console.log(str); // 输出 "Hello from C++!"

JS 传递字符串

#include <emscripten.h>
#include <string.h>

EMSCRIPTEN_KEEPALIVE
int get_string_length(const char* str) {
  return strlen(str);
}
// 为字符串分配内存,并将字符串复制进去
const str = 'Hello Direct Call';
const buffer = Module._malloc(str.length + 1); // 额外字节存放字符串结束符 '\0'
Module.stringToUTF8(str, buffer, str.length + 1); // 将JavaScript字符串转换为UTF8编码存入内存

// 直接调用C函数
const length = Module._get_string_length(buffer);
console.log('String length:', length);

// 务必释放内存!
Module._free(buffer);

编译命令中要加入这段

-sEXPORTED_RUNTIME_METHODS=['UTF8ToString','stringToUTF8']

内存管理是关键

若手动分配内存(如使用Module._mallocModule.stringToUTF8),务必在最后使用Module._free释放,防止内存泄漏。若使用ccall/cwrap并指定'string'参数类型,Emscripten会自动管理临时分配的内存。