作為一種比較新的web技術,WebAssembly可能會對web開發帶來巨大的影響。隨着2月MVP(Minimum Viable Product)版本的發布,WebAssembly的基本特性開始穩定,本文通過一個簡單的示例來演示如何在頁面上調用一個C++函數。
基本組成
要編寫一個可以在瀏覽器中運行的WebAssembly應用,主要分為兩個部分,可編譯為WebAssembly的源語言和加載用的JavaScript。
整個流程大致可以分為:
- 編寫代碼:用常用語言編寫一個工程,可以是C、C++、Rust等。其他各種語言編譯成WebAssembly可以參照這里。
- 編譯:將源碼編譯成WebAssembly的wasm文件。
- 引入:將之前生成的的wasm文件引入到前端工程中。
- 實例化:編寫一段異步JavaScript代碼加載和實例化wasm文件,使其他JavaScript文件可以無障礙使用。
當然,這只是一個非常簡化的流程,但是對於諸如本文的示例項目已經足夠。首先,我們會使用C++編寫一個簡單的函數,然后使用在線工具將其編譯成wasm文件(這樣我們不用下載和安裝任何編譯工具),最后通過14行JavaScript代碼加載並實例化。
上述過程完成之后,我們就能夠通過JavaScript代碼來調用之前通過C++編寫的函數了,是不是很神奇?
編寫代碼
首先我們會編寫一個C++函數,當然了,前面提到過,這里不需要使用任何本地的編譯鏈。取而代之的是一個WebAssembly Explorer的工具。它是一個類似CodePen的工具,可以左側一列貼入C++代碼,編譯后下載對應的wasm文件。當然,類似的在線WebAssembly工具還有Mozilla提供的WebAssembly Studio。
首先打開WebAssembly Explorer頁面,然后在左側粘貼如下C++代碼:
int squarer(int num) { return num * num; }
正如之前提到的,這是一個非常簡單的示例,對於之前沒有學習過C++的人也沒有任何難度。
編譯
下一步就是點擊編譯(“compile”)按鈕,就能看見如下圖所示界面,C++代碼被編譯撐了WAT格式(WebAssembly text format)的文本。
這里不詳述WAT格式,展示該格式內容主要是因為相對於wasm的二進制格式,這種文本格式更易讀。通過WAT文件中可以了解JavaScript和WebAssembly是如何交互的。例如本例中,在export關鍵字后面有_z7squareri這個導出的函數名,因為編譯器對C++函數名的修飾(mangling)。
這里需要特別注意,因為后面使用JavaScript對C++函數進行調用的時候需要依賴這個名字。當然,為了避免這種問題,可以將編譯器選擇為C99或者在函數外層增加extern "C"塊:
extern "C" { int squarer(int num) { return num * num; } }
這樣編譯之后函數名稱不會有變化。編譯成的WAT文件內容如下圖:
此時導出的函數名和原C++函數中的函數名保持一致。
引入
現在點擊下載(“download”)按鈕,並將下載的wasm文件重命名成squarer.wasm。
創建一個文件夾,包含剛才下載的squarer.wasm文件,並新建下面兩個文件:
- index.html:包含標准網頁的模板。
- scripts.js:暫時這是一個空文件,下面就將編寫實例化使用的JavaScript。
實例化
目前為止,HTML標准還不支持將wasm文件像JavaScript文件那樣通過script標簽直接引入。因此為了加載和實例化WebAssembly,需要通過JavaScript異步的調用WebAssembly API來完成這些操作。一共分為三步:
- 將wasm文件加載到數組緩沖區(array buffer)。
- 將加載的數組編譯成WebAssembly模塊。
- 實例化WebAssembly模塊。
實例化之后,JavaScript就可以直接調用剛才WAT文件中export的函數和內存了。
運行
最后當然是期待人心的運行了。先來看看scripts.js內容:
let squarer; function loadWebAssembly(fileName) { return fetch(fileName) .then(response => response.arrayBuffer()) .then(bits => WebAssembly.compile(bits)) .then(module => { return new WebAssembly.Instance(module) }); }; loadWebAssembly('squarer.wasm') .then(instance => { squarer = instance.exports._Z7squareri; console.log('Finished compiling! Ready when you are...'); });
這里的loadWebAssembly函數加載本地的wasm文件並進行實例化,最終返回一個WebAssembly模塊實例。隨后從模塊中獲取導出的函數,隨后即可直接調用。這里需要特別注意,導出的函數名和剛才在WAT文件中看見的符號需要一致。由於C++編譯器對函數名的修飾,建議在對所有需要導出給JavaScript的函數,都加上extern "C"塊,避免函數名混亂導致的不匹配。
在index.html文件中引入scripts.js之后,可以在控制台嘗試調用squarer函數:
通過這個示例,我們了解了如何將C++代碼編譯成WebAssembly並在瀏覽器中執行。我們可以將更多的C、C++、Rust語言編寫的工程放到瀏覽器中運行,以獲得更高的執行效率。當然,還是要再次聲明,這里只是一個簡單的示例,如果函數的參數不是數字,還會有其他問題。隨着WebAssembly工具鏈和集成工具的不斷完善,相信編寫基於WebAssembly的項目一定會更加方便。
本文永久更新鏈接地址:https://www.linuxidc.com/Linux/2018-05/152314.htm