【Emscripten】 C++ でJSの関数を関数ポインタとして使う

TOC

  1. 実行環境
  2. やりたいこと
  3. 手順1: set_js_listener、call_listener 関数を実装
  4. 手順2: ビルドする
  5. 手順3: 関数ポインタをJSで作成する
  6. 手順4: 登録した関数ポインタを呼び出す
  7. コード
  8. 参考

ものすごく久しぶりの投稿となってしまいました。すみません。

今回は、Emscripten で C++ を実行するときに、
JSの関数を関数ポインタとして登録し、それを呼び出せるようにする方法を紹介します。

実行環境

  • Ubuntu 14.04.5 LTS
  • emcc 1.36.0
  • clang version 3.9.0

やりたいこと

  • JS の関数を set_js_listener 関数を使用して登録する
  • call_listener 関数を呼び出した時、登録した JS の関数を実行する

手順1: set_js_listener、call_listener 関数を実装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef void(*JS_LISTENER)();
JS_LISTENER js_listener = NULL;

extern "C"
{
void set_js_listener(JS_LISTENER f)
{
js_listener = f;
}
}

void call_listener()
{
js_listener();
}

EMSCRIPTEN_BINDINGS()
{
emscripten::function("call_listener", &call_listener);
}

typedef を利用して、型を簡略化して使用できるようにしています。

また、set_js_listener は、EMSCRIPTEN_BINDINGS は使用せず、
ビルド時に EXPORTED_FUNCTIONS で js から呼び出せるようにします。
そうしないと、次のようなエラーが出ます。
emscripten_failed_js_pointer
(EMSCRIPTEN_BINDINGS、allow_raw_pointers を使用した場合)

手順2: ビルドする

1
$ emcc main.cpp -std=c++11 -s RESERVED_FUNCTION_POINTERS=1 --bind -s EXPORTED_FUNCTIONS="['_set_js_listener']"

それぞれの引数は、次のような意味があります。

  • std=c++11 を指定しないと、embind を使用する際にエラーが出ます。
  • -s RESERVED_FUNCTION_POINTERS=1 : 1つの関数ポインタを使えるようにします。
  • --bind : embind を使えるようにします。
  • -s EXPORTED_FUNCTIONS="['_set_js_listener']" : set_js_listener を js から呼び出せるようにします。

手順3: 関数ポインタをJSで作成する

1
2
3
4
5
6
var fnPointer = Runtime.addFunction(function() {
console.log("called!");
});

// 登録する
Module.ccall("set_js_listener", "void", [], [fnPointer]);

関数ポインタは、Runtime.addFunction を通して使用します。
set_js_listener は、Module.ccall を使用して呼び出します。

手順4: 登録した関数ポインタを呼び出す

JS から関数ポインタを呼び出します。

1
Module.call_listener();

emscripten_called_js_pointer

called! とコンソールに出力されたので、登録した関数が呼び出されたことがわかります。

コード

今回実行したコードは次のようになりました。

参考

http://stackoverflow.com/questions/12358877/passing-js-function-to-emscripten-generated-code