Edited at

EmscriptenでC言語をJavaScriptに変換する

More than 5 years have passed since last update.

C言語は最強のクロスプラットフォーム言語だと思っていて、

iOSもObjective-Cの中でC言語が使えるし、

AndroidもNDKでC言語が使えるので、

UI以外のロジックはすべてC言語で書けば、それはすべてのプラットフォームで使えるのではないかと思っていました。

しかし、FireFoxOSでは、それができなくて、基本JavaScriptのAPIを呼び出すという形になってC言語が使えませんでした。

失意のどん底に陥っていたのですが、

そこの打開策に、Emscriptenがあったのです!!!!


Emscriptenとは

EmscriptenはC/C++言語からLLVMを生成し、それをJavaScriptに変換するコンパイラのことです。

C言語の標準ライブラリやPOSIXの一部もサポートし、OpenGL ES2.0も使えるそうです。

Emscripten自体は下記にあります。

https://github.com/kripken/emscripten


準備(Mac OSX)

emscriptenを使えるようにします。

いろいろやってみたのですが、SDKをインストールするのが一番楽でした。

手順としては、

http://kripken.github.io/emscripten-site/docs/building_from_source/building_emscripten_from_source_using_the_sdk.html#building-emscripten-from-source-using-the-sdk

に書いてある通りやればよいです。

手順を書いておくと

https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz

から、ソースコードを取得します。

CMakeを使うので

$ brew install cmake

をしておいてください。

コードを解凍後

$  cd emsdk_portable

$ ./emsdk install sdk-incoming-64bit
$ ./emsdk activate sdk-incoming-64bit

とやればよいです。

実行にはnodeとpythonを使うっぽいので、

$ brew install node

とするのと

あとは、python2へシンボリックリンクをつくっておきます

$ sudo ln -s /usr/bin/python2.7 /usr/bin/python2


実行

$ emsdk_portable/emscripten/incoming/emcc first.c -o first.js


とすればJSに変換できます。

(emsdk_portable/emscripten/incoming/にPATHを通しておいた方がいいかも)

たとえば、


first.c

#include <stdio.h>


int
main(void)
{
printf("Hello, World!\n");
return 0;
}

とした場合、

$ emcc first.c -o first.js

$ node first.js
Hello, World!

となります。


Cの関数をJavaScriptから呼ぶ

例えば、


second.c

int

add(int x, int y)
{
return x + y;
}


という関数を外のJavaScriptから呼びたいとします。

そのときは、jsの変換のときに、

$ emcc second.c -s EXPORTED_FUCTIONS="['_add']" -o second.js

という風に公開する関数をEXPORTED_FUCTIONSで指定して上げます。

関数名には最初に"_"を加えて上げます。

あとはjs側からはccallで呼ぶことが可能セス。

ccal(関数名, 戻り値の型, [引数の型], [実際の引数])という形で使用します。


second.html

<html>

<body>
<div id="output"></div>
<script type="text/javascript" src="./second.js"></script>
<script type="text/javascript">
var num = ccall("add", "number", ["number", "number"], [1, 2]);
document.getElementById("output").innerHTML = num;
</script>
</body>
</html>

これで、divに3が出力されます。

また、Module.cwrapを使って関数を取り出す方法もあります。

Module.cwrap(関数名,戻り値の型, [引数の型])

という形で関数を取り出せます。


second2.html

 <html>

<body>
<div id="output"></div>
<script type="text/javascript" src="./second.js"></script>
<script type="text/javascript">
//addという関数を取り出す
var add = Module.cwrap("add", "number", ["number", "number"]);
//取り出した関数を使う
var num = add(1, 2);
document.getElementById("output").innerHTML = num;
</script>
</body>
</html>


こちらも、divに3が出力されています。


JavaScriptをC言語から呼ぶ

逆にCのコードからJSを呼ぶときは、

mergeInto(LibraryManager.library, {

//Cから呼ぶメソッドの定義
});

を使います。

例えば、


thirdlib.js

mergeInto(LibraryManager.library, {

jsPower: function(x) {
return x * x;
}
});


というJavaScriptのファイルと


third.c

#include <stdio.h>

#include <math.h>

int jsPower(int x); //JavaScriptの関数

int
pythagorean(x, y)
{
//JavaScriptの関数jsPowerを呼んで計算
return sqrt(jsPower(x) + jsPower(y));
}

int
main(void)
{
printf("%d\n", pythagorean(3,4));
return 0;
}


というC言語のファイルを用意します。

そこで、--js-libraryオプションでJSコードを指定してコンパイルして実行します。

$ emcc third.c --js-library thirdlib.js -o third.js

$ node thired.js
5

きちんとJSのコードが呼ばれて計算されています。


C++の関数をJavaScriptから呼ぶ

ここからはついでなんですが、

C++にはEMSCRIPTEN_BINDINGSがつかえて、ここに登録しておくと、Module.xxxで関数を呼ぶことができます。


firstcpp.cpp

#include "emscripten/bind.h"


using namespace emscripten;
int
add(int x, int y)
{
return x + y;
}

EMSCRIPTEN_BINDINGS(my_module) {
function("add", &add);
}


として、以下のようにコンパイル(C++だからem++)

$ em++ firstcpp.cpp -o firstcpp.js -std=c++0x --bind

として出力したファイルを使って


firstcpp.html

<html>

<body>
<div id="output"></div>
<script type="text/javascript" src="./firstcpp.js"></script>
<script type="text/javascript">
var num = Module.add(1, 2);
document.getElementById("output").innerHTML = num;
</script>
</body>
</html>


とすれば3が出力されます。


その他


  • C/C++のどこまでの機能が使えるかは未調査

  • 実際にFireFoxOS アプリでどこまで使えるかは未調査

上記もやっていこうと思います。