https://emscripten.org/docs/compiling/WebAssembly.html
WebAssembly默认情况下是异步编译,这意味着您必须先等待编译完成,然后才能调用已编译的代码(通过等待main(),或onRuntimeInitialized回调等)。您可以通过设置来关闭异步编译WASM_ASYNC_COMPILATION=0,但是由于当前的限制,该设置可能无法在Chrome中运行。
关闭了异步编译,获取WebAssembly二进制文件也可能需要异步操作(因为Web不允许在主线程上下载同步二进制文件)。
webwoker:
(function (event) {
importScripts('test.js');
onmessage = function (event)
{
var eventData = event.data;
var res = 0;
switch (eventData.command)
{
case "testThread":
res = Module._testThread();
if(res)
{
postMessage({'function': "testThreadFailed", 'errorCode': res});
}
break;
default:
break;
}
};
})();
test.c
#include
#include
// 实现一个加法
#ifdef __cplusplus
extern "C"{
#endif
int add(int a,int b) {
printf("sum:%i\n",a+b);
return a+b;
}
#ifdef __cplusplus
}
#endif
在emcc中进行编译
emcc -O2 test.c -o test.js –s EXPORTED_FUNCTIONS="['_testThread']"
运行时就可以看到结果
暴漏接口的方法:
#include
// 实现一个加法
EMSCRIPTEN_KEEPALIVE
int add(int a,int b) {
return a+b;
}
emcc add.c -o add.js
当看到add.js和add.wasm文件就说明成功了。add.wasm文件可以说是wasm文件,add.js就是wasm和js文件交互的桥梁。
直接require之前编译好的add.js来运行即可,其中注释的ccall和cwrap需要编译的时候需要暴露方法,具体编译指令也写在注释中。调用方法有三种,如下。
//test.js
方法一:ccall
let addModule = require('./add.js');
addModule.onRuntimeInitialized = function() {
console.log(addModule.ccall('add', 'number', ['number','number'], [3,4]));
}
方法二:cwrap
let addModule = require('./add.js');
let add = addModule.cwrap('add', 'number', ['number','number']);
addModule.onRuntimeInitialized = function() {
console.log(add(1,2))
}
方法三:直接调用
let addModule = require('./add.js');
addModule.onRuntimeInitialized = function() {
console.log(addModule._add(5,6))
}
直接用node运行
node test.js
html文件
add.c文件
#include
#include
#ifdef __cplusplus
extern "C"{
#endif
int EMSCRIPTEN_KEEPALIVE add(int a,int b) {
printf("sum:%i\n",a+b);
return a+b;
}
#ifdef __cplusplus
}
#endif
将add.c编译成add.js文件
emcc add.c -o add.js -O3 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']"
http-server运行后,就可以在浏览器端看到结果
1. 使用编译命令就可以将.cpp文件编译为.html文件,并在网页进行运行:
./emcc tests/hello_function.cpp -o function.html -s EXPORTED_FUNCTIONS="['_int_sqrt']"
2. ①编译完,你可以使用JavaScript调用cwrap()拿到int_sqrt函数。继而可以进行其他操作。
int_sqrt = Module.cwrap('int_sqrt', 'number', ['number'])
int_sqrt(12)
int_sqrt(28)
第一个参数是函数名,第二个参数是函数返回类型,第三个是参数类型。
返回类型和参数类型中可以用类型有三个,分别是number,string和array。number(是js中的number,对应着C中的整型,浮点型,一般指针),string(是JavaScript中的string,对应着C中的char,C中char表示一个字符串),array(是js中的数组或类型数组,对应C中的数组;如果是类型数组,必须为Uint8Array或者Int8Array)。
② 使用.cwrap()的注意事项
A. 导出是在编译阶段做的。比如-s EXPORTED_FUNCTIONS='["_main","_other_function"]' 导出了main()和other_function()。
B. 导出时给函数名加下划线“_”,见A。
C. A中把main也导出了,如果你不导出main,mian就会变成无效代码,这个导出列表应该是完整 的可以keep alive的函数列表。
D. Emscripten会做无效代码清除以减小生成的代码体积,所以请确保导出了所有你想用js调的函数。
E. 如果编译是优化编译-O2级别及以上,会进行代码改编,包括函数名。但是通过-s EXPORTED_FUNCTIONS导出的函数可以继续使用原来的函数名。
F. 如果你想导出一个js库函数(比如,src/library*.js这样的),除了用EXPORTED_FUNCTIONS ,还得用DEFAULT_LIBRARY_FUNCS_TO_INCLUDE。 * 使用Module.ccall调用,不要直接用ccall。前者在代码进行优化编译时也工作。
3. 在JS中直接调C中的函数,不必使用ccall()或cwrap()
①使用编译命令:
emcc api_example.c -o api_example.js
②可以用以下代码执行这个库中的函数:
var em_module = require('./api_example.js');
em_module._sayHi(); // 方法一:直接调用,函数名前要加_
em_module.ccall("sayHi"); // 方法二:使用ccall或cwrap调用,函数名前不需要加_