Emscripten(C/C++) と JavaScript 間の関数呼び出し方法 まとめ

ここのところ、Emscriptenを使ったアプリ開発に関して色々と試していました。今回は、C/C++ と JavaScript間の関数呼び出しや値の受け渡し方法についてまとめたいと思います。

JavaScriptからC/C++のコードを呼び出す

(C/C++側)オプティマイザに消されないようにする

C/C++のコンパイル時に、オプティマイザが関数を削除してしまわないように、コンパイラに指定する必要があります。

以下のように、EXPORTED_FUNCTIONSで個別に関数名(先頭に’_’がつきます)を指定します。また、C言語の関数とするためにC++ないで定義する場合には「extern “C”」が必要です。

もしくは、 -s EXPORT_ALL=1 としてすべて含めることもできます。ただし、何かのライブラリをリンクしていたりすると大きくなるため、あまりおすすめできません。

コンパイル時に指定する以外にも、ソースコードの方から指定することも可能です。「emscripten.h」をインクルードし、エクスポートしたい(削除したくない)関数の前に「EMSCRIPTEN_KEEPALIVE」をつけます。以下のような感じです。

こちらで指定しておけば、コンパイルオプションを書き換える必要はなくなります。数が多い時は、ソースコードに書いたほうが便利かもしれません。

(JavaScript側) ccall または cwrap を使う

C/C++側の関数を直接呼び出すときは、Moduleのccall関数を使います。以下のような感じです。

型名は、”number”か”string”になります。”string”にした場合、JavaScriptの文字列を渡すことができ、C言語側では「const char*」型で受け取ることができます。(ドキュメントによると”array”も使えるらしいですが、まだ試していません)

cwrap関数を使い、JavaScriptの関数オブジェクトにすることもできます。頻繁に使用する関数は、関数オブジェクト化しておくと良いと思います。

 

C/C++からJavaScriptのコードを呼び出す

emscriptenでは、EM_ASMブロックを内に、インラインアセンブラのようにJavaScriptのコードを直接書き込むことができます。注意としては、インライン内では「”」は使えません。そのため必ず「’」を使って下さい。

引数がある場合はEM_ASM_を使います。

JavaScript側では、$0, $1 … のように引数を順番に受け取ることができます。

文字列を渡す場合は、char型のポインタ(おそらくUTF8形式)を渡します。インラインから文字列を得るためには「Pointer_stringify」関数を使用します。なお、変換した文字列は一度変数に入れないとうまく動かないことがありましたので注意が必要です。

バイト配列を渡す場合は、ポインタを渡します。その際、長さも渡しておくと良いでしょう。そして、インラインから以下のようにしてTypeArrayを作成してアクセスします。

この方法を使えば、C/C++側で処理した画像を、JavaScript側のCanvas等に書き戻すことも可能です。調べたところこの方法しか見つかりませんでしたが、メモリ効率がどうなのかは不明です。

JavaScriptから値を返す場合には、EM_ASM_INT(整数を返す)や EM_ASM_DOUBLE(浮動小数点数を返す)を使用します。使い方は、EM_ASM_と同じです。引数を渡さないとエラーになるようです。

この他にも、JavaScriptの関数をC/C++側から直接呼ぶようにすることもできるようです。少々面倒なため、詳細はこちら

参考