前エントリの続き。 blog.kolmas.tech
ライブラリ的なものを作る
前エントリで触れた通り、私がWebAssemblyを知ったきっかけは、Cで実装されたライブラリをクライアントサイドJavaScriptから利用しているものを見た事である。即ち、Cで実装された関数をJavaScript側から任意のタイミングで呼び出せるようにしている。前エントリのHello worldは、単純にCのmain関数が一回実行されただけであった。ここでは、この任意のCの関数を任意のタイミングで呼ぶ、という点に着手したい。
何はともあれ、まずは呼び出される側のCの関数=ライブラリ的なものを作る。
int add(int a,int b){ return a+b; } int sub(int a,int b){ return a-b; }
シンプルに、int型の二引数を取って、その和を返すadd関数と、差を返すsub関数を定義した。
前エントリでも参照したMDN Web Docsの記事によれば、JavaScript側から任意のタイミングで呼び出す関数には、EMSCRIPTEN_KEEPALIVEを付けるそうだ。これは<emscripten/emscripten.h>に定義されている。
#include <emscripten/emscripten.h> EMSCRIPTEN_KEEPALIVE int add(int a,int b){ return a+b; } EMSCRIPTEN_KEEPALIVE int sub(int a,int b){ return a-b; }
これを前回と同様にemccコマンドでコンパイルする。前エントリに引き続き%はプロンプトである。
% emcc samplelib.c -o samplelib.html
一方、以下のEmscriptenの公式ドキュメントには違う方法が説明されている。
emscripten.org
Cのソースコードは元のまま1で、emccコマンドの-sEXPORTED_FUNCTIONSオプションで対象となる関数を指定する。元の関数名の先頭にアンダースコアを付けて指定する。カンマで区切って複数関数を指定できる。
% emcc samplelib.c -o samplelib.html -sEXPORTED_FUNCTIONS=_add,_sub
個人的にはこちらの方が、元のCのソースコードをそのまま使えているという点で好みである。以降の例ではこちらの方式を使うことにする。一応、本エントリ以降の記述については、どちらの方法でコンパイルしても同様に働く事は確認している。
実際に呼び出す・弄り倒す
コンパイルの結果、前エントリのHello worldの例と同様にsamplelib.wasm・samplelib.js・samplelib.htmlが出来ているので、例によってウェブサーバ経由でHTMLファイルにアクセスする。これも前エントリと同様にコンソール風画面が出てくるが、今回はmain関数がないので、そこには何も表示されない。もっとも、作成したadd・sub両関数にしても、標準出力に何かを書き出したりしていないが。
emccコマンドで出力されるJavaScript、ここではsamplelib.jsファイルはグルーコードと呼ぶらしい。WebAssemblyとJavaScriptの間を繋ぐグルー=接着剤。グルーコードは手ずから作ることも当然出来るらしい2が、Emscriptenが出力したものをそのまま使うのが手軽であるようだ。これにはModuleというオブジェクトがあり、このオブジェクトがWebAssembly関連の諸々を担っている。先のHTMLにアクセスしているブラウザ3のコンソールで弄ってみよう。

Moduleオブジェクトの中に_add・_subなるメソッドが見つかる。冒頭にアンダースコアが付いている4が、先程作成したCの関数そのもののようである。呼び出してみよう。

Module._add(3,2)の返り値が5、Module._sub(3,2)の返り値が1、どちらも想定通りの動作である。
こうなるとちょっと遊んでみたくなる。Cと比べたらラフな型システム5であるJavaScriptから、色々な引数を焚べてこのメソッド、ひいてはCの関数を呼び出してみようではないか。


Module._add("3","2")の返り値が5になるあたり、グルーコードは、出来る限り型変換しようとしてくれてはいるようである。Number関数かparseInt関数あたりでも使っているのだろうか。そしてNaNは0扱いである、と。
折角だから和と差だけでなく、四則演算を揃えてみよう。C側で以下のようにmul及びdiv関数を新しく定義し、再度コンパイル6する。
int add(int a,int b){ return a+b; } int sub(int a,int b){ return a-b; } int mul(int a,int b){ return a*b; } int div(int a,int b){ return a/b; }
となれば、やはり次に試してみたいのは0除算である。引数の型変換は問題なく通過するだろうから、C=WebAssembly側でエラーになる筈である。


-
即ち
EMSCRIPTEN_KEEPALIVEを付けず、ひいては<emscripten/emscripten.h>を#includeすることもない。本当に冒頭に掲載したままのCのソースコードそのもの。↩ - 参考。qiita.com↩
- 検証環境はGoogle Chrome。↩
-
コンパイル時の
-sEXPORTED_FUNCTIONSオプションでもアンダースコアを付けている。そういう文化?なのか。↩ - プログラマ視点から見る型システムがラフであるという事は、処理系にしてみたらよっぽど高度な処理をしているという事なのであって、その点でラフと言ってしまうのは心苦しい節はある。↩
-
当然、コンパイル時の
-sEXPORTED_FUNCTIONSオプションには、今回追加した二関数も追加する。↩




