我们走了,我决定尝试牢记一个项目,但是在奉献自己并进行编码之前,总是有一些愚弄一些代码的乐趣。
如果您是WebAssembly的新手,那么Wikipedia之后的第一个可能是MDN Web Docs,它具有不错的介绍背景。它解释说,WebAssembly是二进制格式,并且在所有现代浏览器内部在虚拟机(VM)中运行的相应组装语言。如果您是Unix Philosophy的助剂,这听起来像是亵渎神灵,但否则,它是一种很酷的客户端便携式技术,可以处理高性能数字计算(不,不,我不是在谈论加密货币挖掘)。
)。所以我们应该从哪里开始...
首先是编译器
为了将WebAssembly与我们闪亮的酷数字C/C ++库(您不打算手动编写汇编,不是吗?),我们需要一个编译器,将C/C ++作为输入并生成一个编译器*.wasm
二进制文件以及一些JavaScript胶水代码,需要从网页或node.js调用此代码。好吧,MDN建议使用Emscipten,它围绕llvm clang
编译器,我无法提出合理的论点...
我将跳过如何安装Emscripten SDK的详细信息。只是要注意,使用emcc
编译器的最合理,最理智的方式可能是bash
。但是我决定先尝试Powershell的尝试,而没有其他抽象(例如WSL),它有效...令人惊讶。
让我们编译一些
好吧,让我跳过一个hello world示例,然后以一些在开始项目之前需要解决的基本问题开始:
-
如何与JavaScript连接我的C/C ++代码?至少,我需要调用功能。好吧,对课程,线程等的全力支持会很高兴 - 但也许以后,好吗?
-
如何将任何内容从JavaScript传递到C/C ++。通过几个
double
会很好,但是我需要对Float64
数组的全部支持,我需要传递像struct
s这样的汇总数据。 -
我的C/C ++库如何返回任何有意义的东西而不是单个数字值?
让我们又一步地走一步,经常环顾四周...
使用ccall()
调用C函数7
让我们准备一个非常简单的“库”文件:
// ccall_testing.cpp
#include <cmath>
#include <iostream>
#include <emscripten/emscripten.h>
extern "C" {
EMSCRIPTEN_KEEPALIVE double my_norm(double x, double y) {
std::cout << "my_norm is called\n";
return std::sqrt(x * x + y * y);
}
}
是的,ccall()
是要调用c-功能,因此这里是extern "C"
来处理c ++ name mangling。我们还需要EMSCRIPTEN_KEEPALIVE
来防止将此代码优化为死亡代码。
现在,不要忘记源源所有环境变量,然后使用emcc
:
编译此文件
$emcc ccall_testing.cpp -o ccall_testing.js -sEXPORTED_RUNTIME_METHODS=ccall
我们有两个文件。第一个是WebAssembly Binary ccall_testing.wasm
,第二个是JavaScript胶水代码call_testing.js
。可以将html
用作编译器的目标,它将生成一个网页,但我会坚持使用*.js
以保持控制。
是时候从网页调用我们的库功能了!让我们想出一些非常基本的东西:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My WASM experiments</title>
</head>
<body>
<button id="mybutton">Run</button>
<script>
document.getElementById("mybutton").addEventListener("click", ()=>{
const result = Module.ccall("my_norm", // yes, that's our function
"number", // return type
["number", "number"], // argument types
[3.0, 4.0] // yeah, Pythagorean triple
);
console.log(`result = ${result}`);
});
</script>
<script src="ccall_testing.js" type="text/javascript" async></script>
</body>
将其保存为ccall_testing.html
,然后在眉毛中运行。不起作用吗?还有其他步骤。为了加载*.wasm
blob,我们可以从简单的http-server上提供页面:
$python -m http.server
将在localhost:8000
打开连接。现在只需在您选择的目录中打开html
文件:
http://localhost:8000/wasm_testing/ccall_testing.html
按Run
,打开控制台Ctrl-Shift-I
和 voila!(别忘了Ctrl+R
到重新加载cache):
my_norm is called ccall_testing.js:1559:16
result = 5
喜欢包装而不是打电话?
没问题!可以使用cwrap()
将C-功能直接包装到JavaScript函数中。在这种情况下,我们不再需要EMSCRIPTEN_KEEPALIVE
,因为我们会告诉编译器无论如何要导出此功能,因此我们的cpp
文件变得有点干净
// cwrap_testing.cpp
#include <cmath>
#include <iostream>
#include <emscripten/emscripten.h>
extern "C" {
double my_norm(double x, double y) {
std::cout << "my_norm is called\n";
return std::sqrt(x * x + y * y);
}
}
致电编译器,并在功能名称前面添加一个下划线的-sEXPORTED_FUNCTIONS=_my_norm
(这是一个汇编符号,例如_start
)
$emcc cwrap_testing.cpp -o cwrap_testing.js -sEXPORTED_FUNCTIONS=_my_norm -sEXPORTED_RUNTIME_METHODS=cwrap
我们的html
文件也变得略有不同。在这里,我介绍了一个新的JavaScript函数myNorm
,该功能围绕C-Library函数调用
<button id="mybutton">Run</button>
<script>
document.getElementById("mybutton").addEventListener("click", ()=>{
const myNorm = Module.cwrap("my_norm", // no underscore
'number', // return type
['number', 'number']); // param types;
const result = myNorm(3.0, 4.0);
console.log(`result = ${result}`);
});
</script>
<script src="cwrap_testing.js" type="text/javascript" async></script>
结果与ccall()
相同。那很好。但是,我们如何传达更有意义的东西呢?
但是我需要通过一系列Float64
...
我们可以在函数调用中使用'array'
而不是'number'
吗?类似:Module.ccall("my_norm", "number", "array", [3.0, 4.0])
?
答案“不是那么简单” ...
根据API documentation:“'数组'用于JavaScript数组和输入的数组,包含8位整数数据 - 也就是说,数据写入8位整数的C数组中”。因此,如果我们需要通过一系列double
,则是手动分配或Embind。
是的,艾恩斯很酷!让我们回到这个...