c/c++和js互操作
前些日子看到一个99行c++代码完成的光线追踪渲染器,很好奇,很敬佩,然后顺手移植到html5上了。讲真,h5版挺慢的,然后就想到asm.js可以提速,就移植到asm.js了。移植js过程中,遇到一个蛋疼的问题,就是printf语句。asm.js会在一个沙箱中运行c/c++代码,运行结束后再统一显示输出。为了能显示渲染进度(摸瞎太可怕),我研究了一下从c/c++中调用js代码的方法。项目代码。
Js代码调用编译后的C函数
使用ccall或者cwarp方法
Js调用c/c++代码最简单的方式就是使用ccall()
或者cwarp()
方法。
ccall
方法调用一个编译后的C函数,而cwarp
方法则能返回一个js函数。
举个例子,如下所示c++函数
// tests/hello_function.cpp
#include <math.h>
extern "C" {
int int_sqrt(int x) {
return sqrt(x);
}
}
使用如下命令编译该文件:
./emcc tests/hello_function.cpp -o function.html -s EXPORTED_FUNCTIONS="['_int_sqrt']"
使用cwrap调用
int_sqrt = Module.cwrap('int_sqrt', 'number', ['number'])
int_sqrt(12)
int_sqrt(28)
cwrap的第一个参数是函数名,第二个参数为返回值类型,第三个参数为各个参数类型的数组。
使用ccall调用
// Call C from JavaScript
var result = Module.ccall('int_sqrt', // name of C function
'number', // return type
['number'], // argument types
[28]); // arguments
// result is 5
node.js调用编译后的c/c++代码
c++代码如下:
//api_example.c
#include <stdio.h>
#include <emscripten.h>
EMSCRIPTEN_KEEPALIVE
void sayHi() {
printf("Hi!\n");
}
EMSCRIPTEN_KEEPALIVE
int daysInWeek() {
return 7;
}
编译方法:
emcc api_example.c -o api_example.js
从node.js调用:
var em_module = require('./api_example.js');
em_module._sayHi(); // direct calling works
em_module.ccall("sayHi"); // using ccall etc. also work
console.log(em_module._daysInWeek()); // values can be returned, etc.
Google V8引擎并不支持asm.js,用asm.js并没有加速效果,需要加速直接使用Nodejs Native AddOn编写模块
Js调用c++的类
需要使用webIDL,关于webIDL,我已经写过两篇文章
C/C++代码调用js方法
emscripten_run_script方法
在C/C++代码中调用emscripten_run_script()
方法和js中的eval()
方法一致,将js代码字符串作为参数即可执行。
emscripten_run_script("alert('hi')");
alert
函数只能在命令行使用,更通用的方法还是调用Module.print()
函数用来输出信息
EM_ASM方法
编写内嵌js代码更方便的方法,就是使用EM_ASM()
函数
#include <emscripten.h>
int main() {
EM_ASM(
alert('hello world!');
throw 'all done';
);
return 0;
}
如果需要从C/C++代码向js函数传递参数,可以使用EM_ASM_
函数,注意最后一个_
EM_ASM_({
Module.print('I received: ' + $0);
}, 100);
如果还需要接收返回值,可以使用EM_ASM_INT
或者EM_ASM_DOUBLE
以及其他方法。
int x = EM_ASM_INT({
Module.print('I received: ' + $0);
return $0 + 1;
}, 100);
printf("%d\n", x);
使JS函数变成C语音接口
在library.js中声明函数
mergeInto(LibraryManager.library, {
my_js: function() {
alert('hi');
},
});
在编译时,需要添加上--js-library library.js
。
如果要在c语音中调用my_js
函数,只需要用extern声明接口:
extern void my_js(void);
int main() {
my_js();
return 1;
}
如果要在C++中声明接口,需要使用extern "C" {}
块包裹。
extern "C" {
extern void my_js();
}