使用emscripten實現js直接呼叫C程式碼(emscripten的初探)

zy445566發表於2018-10-24

最近感覺一個時間轉換的C庫挺好用的,但不想做成C擴充套件,並不是說C擴充套件難,對於我來說好歹也是寫過一些C擴充套件的,主要是C擴充套件對環境有一些依賴,比如非win下需要node-gyp做為環境支援來進行編譯,對使用者來說相對麻煩。並且我希望前端也可以呼叫,所以目標瞄準了emscripten,但看了這麼多emscripten的文章,大多都是將理論要不就是翻譯了教程,具體教程本人沒看到。那就自己寫的教程,記錄順便作為emscripten的初探。

emscripten是什麼

大家可能只是熟知emscripten是一個可以將C程式碼轉換成WebAssembly的神器,不僅僅如此,emscripten還可以實現C和js互調,架出一道橋樑。同時核心還是基於LLVM,寫過幾篇關於LLVM的,所以對LLVM製作的軟體還是存在莫名的好感。好廢話不多說,開幹!

emscripten的安裝

這篇文章不細講,可能重要的還是空如何在終端科學上網的內容。點此開啟emscripten的安裝教程

編寫C程式碼

這裡為什麼使用emscripten.h和EMSCRIPTEN_KEEPALIVE的主要原因是我不太想編譯程式碼的時候跟隨一大堆指令,用這個在程式碼裡面看起來也直觀些。當然也可以使用編譯指令EXPORTED_FUNCTIONS來暴露方法。注意如果是C++檔案請用extern "C" 包裹,否則編譯到時方法名會被加上指紋,JS呼叫的話就要根據指紋規則呼叫了

// add.c
#include <emscripten.h>
// 實現一個加法
EMSCRIPTEN_KEEPALIVE
int add(int a,int b) {
    return a+b;
}
複製程式碼

然後進行編譯,指定輸出檔案為add.js否則預設輸出a.out.js

emcc add.c -o add.js -s 
複製程式碼

當看到add.js和add.wasm檔案就說明成功了。add.wasm檔案可以說是wasm檔案,add.js就是wasm和js檔案互動的橋樑。

那麼我們來使用這個C的相加方法

下面直接require之前編譯好的add.js來執行即可,其中註釋的ccall和cwrap需要編譯的時候需要暴露方法,具體編譯指令也寫在註釋中。我們可以使用引用後的檔案加下劃線呼叫方法和ccall和cwrap來呼叫方法的三種方式。其中ccall和cwrap的第一個引數是方法名,第二個引數是方法的返回值,第三個引數是傳入引數的型別。而ccall第四個引數是傳入值並直接執行。cwrap則是先定義方法。onRuntimeInitialized是初始化模組。

// test.js
// 如果要解注,編譯的的使用請使用 emcc add.c -o add.js -s -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
let addModule = require('./add.js');
// let add = addModule.cwrap('add', 'number', ['number','number']);
addModule.onRuntimeInitialized = function() {
    // console.log(add(1,2))
    // console.log(addModule.ccall('add', 'number', ['number','number'], [3,4]));
    console.log(addModule._add(5,6))
}
複製程式碼

可以直接用node執行,我目前的node版本是10,低版本沒測試過。

node test.js # 結果11
複製程式碼

就此完成了使用emscripten實現js直接呼叫C程式碼的過程,本教程程式碼可以點此檢視

最後

emscripten確實使用了一個很棒的思路來解決JS呼叫C語言的問題,wasm真香。其他語言也快來,轉成wasm來被JS支配吧!

相關文章