● JavaScript から C関数を呼び出す
ccall() を使います。
(C/C++言語から JavaScript Lib を呼び出す方法は こちら)
#include
#include
extern "C" void myfunc01()
{
printf( "MYFUNC01\n" );
}
int main()
{
EM_ASM(
// C言語関数の呼び出し
ccall( 'myfunc01', 'v' );
);
return 0;
}
C/C++側から参照がない関数はオプティマイザが削除してしまうので
そのままだと上のプログラムはエラーになります。
コンパイル時に下記のオプションが必要です。
-s EXPORTED_FUNCTIONS="['_main','_malloc','_myfunc01','_myfunc02']
EXPORTED_FUNCTIONS で個別に関数名を指定するか、または
-s EXPORT_ALL=1
を指定します。
C言語の symbol なので内部表現では先頭に ‘_’ が付きます。
C++ 内で定義する場合は extern “C” が必要な点に注意。
● 引数の指定と cwrap
ccall 2番目の引数 ‘v’ は戻り値の型を意味しています。
例えば文字列を返す関数の場合 ‘string’ を与えます。
#include
#include
extern "C" const char* myfunc01()
{
return "MYFUNC_01";
}
int main()
{
EM_ASM(
// C言語関数の呼び出し
console.log( ccall( 'myfunc01', 'string' ) );
);
return 0;
}
‘string’ は自動的にオブジェクトに変換されます。
引数を渡す場合も型の情報が必要です。
#include
#include
extern "C" const char* myfunc02( int a, float b, const char* c )
{
printf( "%d %f %s\n", a, b, c );
return "MYFUNC_02";
}
int main()
{
EM_ASM(
// C言語関数の呼び出し
console.log( ccall( 'myfunc02', 'string', ['number','number','string'], 12, 34.5, 'abcdefg' ) );
);
return 0;
}
文字列引数はポインタに変換するために、一旦内部の仮想メモリ空間に
展開する必要があります。
JavaScript から呼び出した場合、この領域として stack が用いられています。
戻り値 引数 C言語 --------------------------------------------- string string const char* number number int/float 他 (int64以外) v -- void
頻繁に呼び出す場合は cwrap() で関数に変換できます。
var myfunc02= cwrap( 'myfunc02', 'string', ['number','number','string'] );
var ret_str= myfunc02( 345, 67.8, 'hijklmn' );
● IDBFS ファイルシステム
ブラウザのローカルストレージに保存可能なファイルシステムです。
任意の場所に mount 可能で、path + filename がそのまま key になります。
通常は仮想 FS に対して読み書きを行うので IDB との同期が必要です。
FS.syncfs( true, callback ); 読み込み IDB → Virtual FS FS.syncfs( false, callback ); 書き込み Virtual FS → IDB
void save_func()
{
FILE* fp= fopen( "/save/data.txt", "w" );
fputs( "ABCDEFG", fp );
fclose( fp );
EM_ASM(
// 書き込み (false は省略できる)
FS.syncfs( false, function(err){ } );
);
}
// EXPORTED_FUNCTIONS に '_load_func' を加える必要あり
extern "C" void load_func()
{
// 読み込みが完了したらアクセスできる
EM_ASM(
var text= FS.readFile( '/save/data.txt', {flags:'r',encoding:'utf8'} );
console.log( text );
);
}
static void ems_loop()
{
static int count= 0;
if( ++count == 60 * 3 ){
save_func();
}
}
int main()
{
EM_ASM(
FS.mkdir( '/save' );
FS.mount( IDBFS, [], '/save' );
// 読み込み
FS.syncfs( true, function(err){
ccall( 'load_func', 'v' );
});
);
emscripten_set_main_loop( ems_loop, 60, true );
return 0;
}
callback を使うので mail_loop が必要です。
いろいろ試していますが通常の C関数での読み込みがうまく行っていません。
JavaScript からは可能でした。
● NODEFS
ブラウザのように制限が無いため、node command の場合は NODEFS を
使って直接ローカルファイルにアクセスできます。
EM_ASM(
FS.mkdir( '/save' );
FS.mount( NODEFS, {root:'.'}, '/save' );
);
mount 時の root option が実際の File System の Path 指定になります。
syncfs は不要。
● SOCKFS
read, write, open, close 等の標準 API で socket にアクセスできるようにするため、
Socket API は FileSystem の一種として実装されているようです。
実装上の内部的なものであって特に使用する必要はありません。
● 独自ファイルシステムを作る
MountType は FileSystem Object なので、
自分で FileSystem を作ることも可能です。
// chafs.js
mergeInto( LibraryManager.library, {
chafs_init: function(){
Module.CHAFS= {
createNode: function( parent, name ){
var node= FS.createNode( parent, name, 511 ); // 0777
node.node_ops= {
lookup: function( parent, name ){
return Module.CHAFS.createNode( parent, name );
},
};
node.stream_ops= {
open: function( stream ){
},
close: function( stream ){
},
read: function( stream, buffer, offset, length, position ){
// buffer を 'A' で埋めるだけ
for( var i= 0 ; i< length ; i++ ){
buffer[offset + i]= 65;
}
return length;
},
};
return node;
},
mount: function( mount ){
return Module.CHAFS.createNode( null, '/' );
},
};
},
});
chafs_init() で FileSystem を作成しています。
// main.cpp
#include
#include
extern "C" void chafs_init();
static void load_func()
{
FILE* fp= fopen( "/save/data.txt", "r" );
char buf[128];
size_t size= fread( buf, 1, 10, fp );
buf[size]= '\0';
fclose( fp );
printf( "%s\n", buf );
}
int main()
{
chafs_init(); // 作成
EM_ASM(
FS.mkdir( '/save' );
FS.mount( Module.CHAFS, [], '/save' );
);
load_func();
return 0;
}
この例では CHAFS をマウントした '/save' 以下どのファイルを読み出しても、
すべて 'A' で埋められています。
ただし getattr を定義していないのでファイルサイズを取ることはできません。
関連エントリ
・Emscripten C++ のアプリをブラウザで動かす (8) iOS でも動く
・Emscripten C++/OpenGL ES 2.0 (7) Emscripten の OpenGL API と WebGL
・Emscripten C++/OpenGL ES 2.0 (6) Chrome の速度と IE11/Safari
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (5)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (4)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (3)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (2)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (1)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす 一覧