這會將 JS 繫結至 wasm!
上一篇Wasm 文章提到了我 瞭解如何將 C 程式庫編譯為 wasm,以便在網路上使用。有一件事 我 (以及許多讀者) 都很滿意這一點 您必須手動宣告自己使用的 wasm 模組中的哪些函式。 為複習一下,我討論的是以下程式碼片段:
const api = {
version: Module.cwrap('version', 'number', []),
create_buffer: Module.cwrap('create_buffer', 'number', ['number', 'number']),
destroy_buffer: Module.cwrap('destroy_buffer', '', ['number']),
};
我們在此宣告使用
EMSCRIPTEN_KEEPALIVE
、傳回類型,以及其類型
引數詞之後,我們可以使用 api
物件的方法叫用
這些函式。不過,以這種方式使用 wasm 並不支援字串
您需要手動移動記憶體區塊
因此需要大量建立程式庫
API 非常繁瑣有更好的方法吧?是的話,為什麼會
這篇文章會說明什麼內容?
C++ 名稱管理
雖然開發人員體驗值得一提
而要打造實用工具
其實,當您編譯 C 時
或 C++ 程式碼,每個檔案都會分開編譯。接著連結器會處理
將所有這類所謂的物件檔案傳遞在一起,然後轉換成 am。
檔案。使用 C 時,仍可在物件檔案中使用函式名稱
供連結器使用你只需呼叫 C 函式即可
我們以字串形式提供給 cwrap()
。
另一方面,C++ 支援函式超載,表示只要實作
在簽章不同的情況下多次相同的函式 (例如
不同的類型參數)。在編譯器層級,有一個不錯的名稱,例如 add
會「破壞」為用來編碼函式簽章的內容
連結器的名稱因此,我們無法查詢函式
命名
輸入 Ebind
embind 是 Emscripten 工具鍊的一部分,提供了多種 C++ 巨集 可讓您為 C++ 程式碼加上註解您可以宣告哪些函式、列舉 您打算從 JavaScript 使用的類別或值類型。開始 幾個簡單函式來操作
#include <emscripten/bind.h>
using namespace emscripten;
double add(double a, double b) {
return a + b;
}
std::string exclaim(std::string message) {
return message + "!";
}
EMSCRIPTEN_BINDINGS(my_module) {
function("add", &add);
function("exclaim", &exclaim);
}
與上一篇文章相比,我們不再納入emscripten.h
,
我們不必再使用 EMSCRIPTEN_KEEPALIVE
為函式加上註解
而是 EMSCRIPTEN_BINDINGS
部分,其中列出在
我們想將函式提供給 JavaScript
如要編譯這個檔案,我們可以使用相同的設定 (或者,您也可以用同樣的設定
Docker 映像檔),如上一個
一文。如要使用 embind,
我們會加上 --bind
標記:
$ emcc --bind -O3 add.cpp
現在我們要做完最後一步,讓 HTML 檔案能夠 已建立 wasm 模組:
<script src="/a.out.js"></script>
<script>
Module.onRuntimeInitialized = _ => {
console.log(Module.add(1, 2.3));
console.log(Module.exclaim("hello world"));
};
</script>
如您所見,我們不再使用 cwrap()
。這只會直接
方塊內。更重要的是,我們無需擔心手動複製
讓字串正常運作!embind 可讓您免費取得
具有類型檢查:
這項功能很實用,因為我們能及早發現部分錯誤,而不必處理 偶爾出現很不準的真的是錯的
物件
許多 JavaScript 建構函式和函式都會使用選項物件。很棒 模式,但手動操作時卻無所適從。碎形 也可以在此提供協助!
例如,我提出了這個「非常」實用的 C++ 函式,可以處理我的 因此我急著想在網路上使用。方法如下:
#include <emscripten/bind.h>
#include <algorithm>
using namespace emscripten;
struct ProcessMessageOpts {
bool reverse;
bool exclaim;
int repeat;
};
std::string processMessage(std::string message, ProcessMessageOpts opts) {
std::string copy = std::string(message);
if(opts.reverse) {
std::reverse(copy.begin(), copy.end());
}
if(opts.exclaim) {
copy += "!";
}
std::string acc = std::string("");
for(int i = 0; i < opts.repeat; i++) {
acc += copy;
}
return acc;
}
EMSCRIPTEN_BINDINGS(my_module) {
value_object<ProcessMessageOpts>("ProcessMessageOpts")
.field("reverse", &ProcessMessageOpts::reverse)
.field("exclaim", &ProcessMessageOpts::exclaim)
.field("repeat", &ProcessMessageOpts::repeat);
function("processMessage", &processMessage);
}
我要針對 processMessage()
函式的選項定義結構體。在
EMSCRIPTEN_BINDINGS
區塊,我可以使用 value_object
進行 JavaScript 觀察
將這個 C++ 值當做物件使用我也可以使用「value_array
」
並將這個 C++ 值用做陣列。我也繫結 processMessage()
函式,且
其餘部分會繫結至「magic」。現在可以從processMessage()
不含任何樣板程式碼的 JavaScript:
console.log(Module.processMessage(
"hello world",
{
reverse: false,
exclaim: true,
repeat: 3
}
)); // Prints "hello world!hello world!hello world!"
類別
為了保持完整性,我應該也示範 整個類別,並帶來許多與 ES6 類別的協同作業。您或許能獲得 立即開始看到模式:
#include <emscripten/bind.h>
#include <algorithm>
using namespace emscripten;
class Counter {
public:
int counter;
Counter(int init) :
counter(init) {
}
void increase() {
counter++;
}
int squareCounter() {
return counter * counter;
}
};
EMSCRIPTEN_BINDINGS(my_module) {
class_<Counter>("Counter")
.constructor<int>()
.function("increase", &Counter::increase)
.function("squareCounter", &Counter::squareCounter)
.property("counter", &Counter::counter);
}
在 JavaScript 上,這幾乎就像是原生類別:
<script src="/a.out.js"></script>
<script>
Module.onRuntimeInitialized = _ => {
const c = new Module.Counter(22);
console.log(c.counter); // prints 22
c.increase();
console.log(c.counter); // prints 23
console.log(c.squareCounter()); // prints 529
};
</script>
那 C 呢?
embind 是專為 C++ 所編寫,且只能用於 C++ 檔案,但無法用於
代表您無法連結到 C 檔案!如要混合使用 C 和 C++,您只需要
將輸入檔案分成兩個群組:一個用於 C,另一個用於 C++ 檔案。
擴充 emcc
的 CLI 標記,如下所示:
$ emcc --bind -O3 --std=c++11 a_c_file.c another_c_file.c -x c++ your_cpp_file.cpp
結論
embind 大幅改善開發人員體驗 搭配 wasm 和 C/C++本文並未涵蓋所有方案的合併選項。 有興趣的話,建議您繼續處理 embind's 說明文件。 請記住,使用 embind 可同時讓 wasm 模組和 JavaScript 的黏合程式碼在 gzip 時放大到最高 11k (尤其是在 模組。如果您只有非常小的駝峰表面,繫結成本可能會高於 值得在正式環境中使用!儘管如此 只要試用看看