前一篇笔记写了如何在js中调用c++的函数,要在c++里执行js代码也有几种方法,另外和从js到c++的交互只能调用函数和操作内存不同,c++里可以编写完整的js代码并获得结果。
这里依然先附上官方的参考:https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html
使用emscripten_run_script直接执行代码
这是emscripten提供的执行js代码的方法,此方法无法获取js代码的运行结果(类似于返回值),可以获取返回值的版本在下文。这个方案的内部实现方式是把这个代码字符串扔外面的js环境用eval()来执行。
在test.cpp中编写如下代码:
#include <emscripten.h> int main() { emscripten_run_script( "console.log('岂因祸福避趋之');\n" "console.log('6');"); return 0; }
然后执行命令编译:emcc -sMODULARIZE=1 -sASSERTIONS -sEXPORT_ES6 -o dist/test.mjs test.cpp
接着修改start.js:
(async () => { const { default: wasm } = await import('./dist/test.mjs'); const instance = await wasm(); /* 控制台输出: 岂因祸福避趋之 6 */ })();
这个方法还有2个变体,以解决不能获取返回值的问题:
举例:
#include <emscripten.h> #include <iostream> using namespace std; int main() { int a = emscripten_run_script_int("1+1;");//方法将返回脚本中最后一个表达式的结果 cout << "a:" << a << endl; //输出:"a:2" char* str=emscripten_run_script_string("'niconiconi~ 妮可妮可妮~'");//方法将返回脚本中最后一个表达式的结果 cout << "str:" << str << endl; //输出:"str:niconiconi~ 妮可妮可妮~" return 0; }
以上方法如果在执行js代码的过程中遇到错误将会导致执行直接中断,我尝试了几个编译参数没有找到能在c++中捕获其错误的方法,但按理说应该是有的。
使用EM_JS、EM_ASYNC_JS、EM_ASM执行Javascript代码
文档中写这类方法比emscripten_run_script那类的速度要快,但没说明是调用开销小还是执行效率更高。这类方法的本质是在宏内定义js函数体的代码。
EM_JS和EM_ASYNC_JS是通过宏构造c++胶水函数,然后在需要使用的时候去调用它。一般的js代码就直接用EM_JS,如果代码中存在await,则需要使用EM_ASYNC_JS。
EM_ASM则是在编写该宏的位置执行js代码,我没有找到它的async版本。EM_ASM系列的宏可以使用占位符添加参数,见示例。
为了能够获得EM_ASM中脚本执行的返回值,其也有几个变体:
在使用EM_ASYNC_JS时需要在编译参数中添加-sASYNCIFY,C++中嵌入的异步脚本执行是靠这个参数添加的库实现的。
现在改写test.cpp:
#include <emscripten.h> EM_JS(void, call_console, (), { console.log('从call_console调用'); }); EM_ASYNC_JS(unsigned long long, delay_test, (), { console.log('开始执行EM_ASYNC_JS:' + Date.now()); const promise = new Promise((resolve, reject) = > { setTimeout(resolve, 1000); }); await promise; console.log('结束执行EM_ASYNC_JS:' + Date.now()); return Date.now(); }); int main() { call_console(); // 调用使用EM_JS宏构造的call_console函数 auto time = delay_test(); // 调用使用EM_ASYNC_JS宏构造的call_console函数 EM_ASM({ console.log('从EM_ASM调用'); }); //支持返回值的脚本执行,并且在后面添加参数,$0是第一个参数占位符,$1是第二个参数占位符,以此类推 int x = EM_ASM_INT({ return $0 + 22; }, 11); //x=33 return 0; }
一个重要提示:如果你使用编辑器的代码格式化功能,请注意js中的箭头函数 “=>” 在C++代码中可能会被格式化为 “= >” ,这会导致js执行报错。
然后执行命令编译:emcc -sMODULARIZE=1 -sASSERTIONS -sEXPORT_ES6 -sASYNCIFY -o dist/test.mjs test.cpp
,start.js中只要加载wasm即可,无需执行其它代码,运行后将得到以下输出:
从call_console调用 开始执行EM_ASYNC_JS:1723741398524 (停顿1秒) 结束执行EM_ASYNC_JS:1723741399533 从EM_ASM调用
本文发布于 https://luojia.me
本站文章未经文下加注授权不得拷贝发布。