EmscriptenでJSとCで相互にデータをやり取りする
Emscripten で JavaScript の世界と C の世界でデータをやり取りする方法をメモ (with ccall/cwrap)。
C や C++ 側で必要なこと
Emscripten でコンパイルすると、main
から到達できないコードは dead code elimination でサクサク消されちゃうので
関数に EMSCRIPTEN_KEEPALIVE
を付けて消されないようにしておく必要がある
(EMSCRIPTEN_KEEPALIVE
自体は __attribute__((used))
に展開されるっぽい。環境によるとは思うけど)。
コンパイルは
emcc -s WASM=1 -s NO_EXIT_RUNTIME=1 -s EXPORTED_RUNTIME_METHODS="['ccall']" -o index.html main.cc
とかで。NO_EXIT_RUNTIME
にしておくことで、 main
を実行したあとランタイムを止めるというデフォルトの挙動を変更できる。
ExPORTED_RUNTIME_METHODS
に cwrap
とかを入れておくと cwrap から使えるようになる。
ccall や cwrap で関数を呼び出すときの基本的な方法
Module.cwrap('hoge', 'string', 'number')(5);
みたいな感じで C の関数を呼び出せるが、問題は引数と戻り値のところに何を入れるかということになる。
一応使える型は表のような感じらしい。
名前 | 用途 |
---|---|
'string' |
C 文字列を JavaScript 側に渡すときに指定できる (それらしい記述は見当たらなかったが、wasm の世界に渡す向きはちゃんと動かないっぽい (エラーは出ないが)) |
'array' |
単純な配列か Uint8Array か Int8Array を C 側に渡す (戻り値側に書くとエラー) |
'number' |
整数、実数、ポインタ何にでも使える。具体的な型は意識しなくていいっぽい。 |
ポインタについて
ポインタは HEAPU8
の配列のインデックスになる。JavaScript 側で HEAPU8
から malloc
, free
するには
Module._malloc
, Module._free
する。ヒープにデータを置くときは HEAPU8.set()
する。
あと同じ配列への参照で HEAP8
(符号付き8ビット整数)、HEAP32
とかいろいろあるっぽい。ビット幅が違う参照はインデックスも
変わるので注意。
これ知ってるだけで (推奨される方法かどうかはさておき) 一通り動くものはできそう。