目次
- 本記事の目的
- C/C++で呼び出したい関数を実装する
- emccのExportedFunctionsオプションを使ってビルド
- JSからwasmを読み込んで関数を呼び出す
- まとめ
1. 本記事の目的
本記事では、C/C++で実装した関数をJSから呼び出す方法を紹介します。
例として、int型の引数を2つとって和を返すadd(int, int)を実装します。
GitHubにソースを上げました。
wasm-sample/02-wasm-exported-functions at master · shogonir/wasm-sample · GitHub
2. C/C++で呼び出したい関数を実装する
add(int, int)をCとC++で実装します。
2.1. C言語で実装する
int add(int a, int b) { return a + b; }
2.2. C++で実装する
C++の場合はマングリングを回避するためにextern “C"を忘れないようにしてください。
extern "C" { int add(int a, int b) { return a + b; } }
3. emccのEXPORTED_FUNCTIONSオプションを使ってビルド
dockerコンテナ内でC/C++からwasmをビルドするため、cpp2wasm.shを作成します。
emcc add.c \ -s WASM=1 \ -s "MODULARIZE=1" \ -s "EXPORTED_FUNCTIONS=['_add']" \ -o add.js
EXPORTED_FUNCTIONSの右辺はシングルクォートの文字列のリストです。
実装した関数の先頭にアンダースコアをつけましょう。
C++の場合はadd.cの部分をadd.cppに変えるだけで大丈夫です。
上記のcpp2wasm.shをdockerコンテナ内で実行するcompile.shを作成します。
docker run --rm -t -v $(pwd):/src gifnksm/emscripten-incoming sh cpp2wasm.sh
この状態でcompile.shを実行するとadd.js, add.wasmが生成されるはずです。
4. JSからwasmを読み込んで関数を呼び出す
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>modularize</title> </head> <body> <script src="add.js"></script> <script> var module; fetch('add.wasm') .then(response => response.arrayBuffer()) .then(buffer => new Uint8Array(buffer)) .then(binary => { var moduleArgs = { wasmBinary: binary, onRuntimeInitialized: function () { var add = module.cwrap('add', 'number', ['number', 'number']); console.log(add(2, 3)); } }; module = Module(moduleArgs); }); </script> </body> </html>
module = Module()とした後、onRuntimeInitializedコールバック内で関数を取り出して実行しています。
関数を取り出すにはmodue.cwrap()を実行します。
cwrapの第一引数は取り出したい関数名、第二引数は返り値の型、第三引数は引数の型です。
このcwrapの返り値はC/C++で実装したfunctionになっています。
5. まとめ
今回はwasmの関数をJSから呼び出す方法を紹介しました。
下記を注意してください。
- C++で実装する場合はextern “C"する
- ビルド時のEXPORTED_FUNCTIONSの時だけ関数名の先頭にアンダースコアをつける
- cwrapで関数を取り出すときはonRuntimeInitializedかmainが発火してから