[WebAssembly]初学笔记 在C++中嵌入Javascript代码

如果有看不明白的地方请先看前置说明文章

前一篇笔记写了如何在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_JSEM_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

本站文章未经文下加注授权不得拷贝发布。

0 0 投票数
打分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论