はじめに
前回は、C++の実行結果をhtmlに反映させました。今回は、JavaScriptの関数をC++に公開してC++内でhtmlを変更します。これによって複雑な計算をC++で行って直ぐにhtmlに反映させるようなことができてJavaScriptとC++の垣根が低くなるかと思います。
環境
- windows 10
- python:3.6.5
- emcc:1.39.16
- clang:11.0.0
Webアセンブリファイルを作成する
まずは、Webアセンブリ用のC++を作成してコンパイルをします。今回はC++のソースとコンパイルがそれぞれ変わっています。
C/C++ファイルの作成
例としてテキストボックスに数字を入れる関数(set_text)を想定します。C++ではextern void set_text(int x)
で関数の定義だけしています。そして、使用したい関数(今回はmain)内でその関数を呼び出します。
#include <emscripten/emscripten.h>
extern "C" {
extern void set_text(int x);
}
int main() {
set_text(1111);
return 0;
}
C/C++ファイルのコンパイル
Emscripten用のコマンドプロンプトを起動してコンパイルをします。コマンドプロンプトの起動などは前回を見てください。コンパイル時に -s SIDE_MODULE=1
と-O1
のオプションを追加します。 -s SIDE_MODULE=1
はサイドモジュールのオプションになります。-O1
は、最適化オプションで必須ではないですがimportObjectの設定が手間なので入れます。
コマンド
em++ 入力C++ファイル -s SIDE_MODULE=1 -O1 -o 出力wasmファイル
ちなみにWASMは入れていません。デフォルトが1なのでいらないことに気が付きました。
>em++ cmain.cpp -s SIDE_MODULE=1 -O1 -o hello.wasm
>
コマンドが終わると同フォルダ内にhello.wasmができているはずです。
wasmを呼び出すhtmlを作成する
前回のソースに加えてimportObjectの情報を追加して、C++のmain関数は戻り値を使用せずに単純に呼び出すだけにします。
importObjectの設定
前回と同じように確認した情報をimportObjectに設定していきます。その際に連想配列の関数にC++へ公開する関数の処理を書いていきます。今回はoutputに引数を格納しています。
var importObject = {
wasi_snapshot_preview1: { proc_exit:args => console.log(arg) },
env: {
set_text: arg => document.getElementById("output").value = arg,
} };
インポート後の処理
インポート後の情報はobjに格納されているため、obj.instance.exports.main()でC++の関数を実行するだけにしています。
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>WebAssembly</title>
</head>
<body>
<input type="button" id="test_view" value="表示" /><input type="text" id="output"/>
<script>
var importObject = {
wasi_snapshot_preview1: { proc_exit:args => console.log(arg) },
env: {
set_text: arg => document.getElementById("output").value = arg,
} };
document.getElementById('test_view').addEventListener('click', () => {
WebAssembly.instantiateStreaming(
fetch('hello.wasm'), importObject
).then(obj => obj.instance.exports.main());
}, false
);
</script>
</body>
</html>
実行
確認用のサーバを再起動して上のhtmlを実行します。表示ボタンを押すとC++のmainが実行されていset_text()が実行されて与えていた1111が表示されました。うまくC++からJavaScriptの関数を実行することができました。
JQueryを使用してみる
JavaScriptをC++に公開しているのでJavaScriptのライブラリであるJQueryも公開できるはずと思いJQueryを使用してみました。
JQueryのダウンロード
npmを使えるので、npm経由でJQueryをダウンロードしました。
コマンド
npm install jquery
フォルダ内にnode_modulesができました。
JQueryをhtmlに適用
JQueryを使用してテキストを入れ替えます。先ほどの例に<script>
と$("#output").val(arg)
を追加しました。
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>WebAssembly</title>
</head>
<body>
<input type="button" id="test_view" value="表示" /><input type="text" id="output"/>
<script>
var importObject = {
wasi_snapshot_preview1: { proc_exit:args => console.log(arg) },
env: {
set_text: arg => $("#output").val(arg),
} };
document.getElementById('test_view').addEventListener('click', () => {
WebAssembly.instantiateStreaming(
fetch('hello.wasm'), importObject
).then(obj => obj.instance.exports.main());
}, false
);
</script>
<script type="text/javascript" src="node_modules/jquery/dist/jquery.min.js"></script>
</body>
</html>
実験
サーバを立てて試してみたところ先ほどと同じように追加できました。JQueryも無事C++に公開することができました。
おわりに
JavaScriptの関数をC++に公開して実行できるようにしてみました。ぱっと見はそこまで恩恵はないように思いますが、処理速度が速くなっていたり、メモリ消費が抑えられていると恩恵は大きくなります。次回はそこら辺を実験してみようかと思います。