blob: eb8d5bdf61db4fb61990587a5a1ecec9a6790d15 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
---
title: C/C++からWebAssemblyにコンパイルする
slug: WebAssembly/C_to_wasm
translation_of: WebAssembly/C_to_wasm
---
<div>{{WebAssemblySidebar}}</div>
<p class="summary">C / C ++のような言語でコードを書いたら、<a href="/ja/docs/Mozilla/Projects/Emscripten">Emscripten</a> のようなツールを使って WebAssembly にコンパイルすることができます。 どのように動作するかを見てみましょう。</p>
<h2 id="Emscripten_の環境設定">Emscripten の環境設定</h2>
<p>まず、必要な開発環境をセットアップしましょう。</p>
<h3 id="準備">準備</h3>
<p>Emscripten SDKを取得します。以下の指示にしたがってください。<a href="https://emscripten.org/docs/getting_started/downloads.html">https://emscripten.org/docs/getting_started/downloads.html</a></p>
<h2 id="サンプルコードをコンパイルする">サンプルコードをコンパイルする</h2>
<p>環境を設定した後は、C のサンプルコードを Emscripten にコンパイルする方法を見てみましょう。 Emscripten でコンパイルするときにはいくつかのオプションがありますが、この記事でカバーする主な2つのシナリオは次のとおりです:</p>
<ul>
<li>wasm にコンパイルし、コードを実行するための HTML とウェブ環境上で wasm を実行するための全ての JavaScript グルーコードを生成する。</li>
<li>wasm にコンパイルと JavaScript の生成だけ行う。</li>
</ul>
<p>2つについて見てみましょう。</p>
<h3 id="HTML_と_JavaScript_を生成する">HTML と JavaScript を生成する</h3>
<p>最も簡単なケースを見てみましょう。コードを WebAssembly としてブラウザで実行するための全てを Emscripten で生成するようにします。</p>
<ol>
<li>まずはコンパイルするためのサンプルコードを用意します。以下のCのサンプルコードをコピーして <code>hello.c</code> としてローカルドライブの新しいディレクトリに保存してください:
<pre class="brush: cpp notranslate">#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello World\n");
}</pre>
</li>
<li>Emscripten コンパイラ環境を導入したターミナルウィンドウを使用して、<code>hello.c</code> ファイルと同じディレクトリに移動して、次のコマンドを実行します:
<pre class="brush: bash notranslate">emcc hello.c -s WASM=1 -o hello.html</pre>
</li>
</ol>
<p>このコマンドで渡されたオプションは次のとおりです:</p>
<ul>
<li><code>-s WASM=1</code> — 出力を wasm に指定します。指定しない場合、Emscripten はデフォルトでは <a href="http://asmjs.org/">asm.js</a> として出力します。</li>
<li><code>-o hello.html</code> — コードを実行するための HTML ページを指定します。wasm モジュールとそれをウェブ環境で使用できるようにコンパイル、インスタンス化するための JavaScript グルーコードも出力に含まれます。</li>
</ul>
<p>この時点でソースディレクトリに以下のファイルが出力されているはずです:</p>
<ul>
<li>バイナリの wasm モジュールコード (<code>hello.wasm</code>)</li>
<li>ネイティブの C の関数と JavaScript/wasm の間で変換を行う JavaScript ファイル (<code>hello.js</code>)</li>
<li>wasm コードをロード、コンパイル、インスタンス化し、ブラウザに出力するための HTML ファイル (<code>hello.html</code>)</li>
</ul>
<h3 id="サンプルコードを実行する">サンプルコードを実行する</h3>
<p>WebAssembly をサポートしているブラウザで <code>hello.html</code> をロードするだけです。WebAssembly は Firefox 52+ と Chrome 57+/最新の Opera でデフォルトで有効になっています (Firefox 47+では <em>about:config</em> で <code>javascript.options.wasm</code> flag を有効にすることで、Chrome (51+) と Opera (38+) では <em>chrome://flags</em> に飛んで <em>Experimental WebAssembly</em> フラグを有効にすることで wasm コードを実行することができます) 。</p>
<p>全てが計画通りに機能していれば、ウェブページ上の Emscripten コンソールに "Hello world" の出力が表示されるはずです。おめでとうございます、ようやくCを WebAssembly にコンパイルしてブラウザで実行することができました!</p>
<h3 id="カスタム_HTML_テンプレートを使う">カスタム HTML テンプレートを使う</h3>
<p>場合によっては、カスタム HTML テンプレートを使用することもできます。 どうやってできるかを見てみましょう。</p>
<ol>
<li>
<p>まず、次の C のコードを <code>hello2.c</code> として新しいディレクトリに保存します:</p>
<pre class="brush: cpp notranslate">#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello World\n");
}</pre>
</li>
<li>
<p><code>shell_minimal.html</code> をあなたの emsdk レポジトリから探します。先程作成した新しいディレクトリに <code>html_template</code> というサブディレクトリを作って、そこにコピーします。</p>
</li>
<li>
<p>新しいディレクトリに移動して (Emscripten コンパイラ環境があるターミナルウィンドウで) 、次のコマンドを実行します:</p>
<pre class="brush: bash notranslate">emcc -o hello2.html hello2.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html</pre>
<p>今回渡したオプションは少しだけ異なります:</p>
<ul>
<li><code>-o hello2.html</code> と指定したことで、今回コンパイラは JavaScript グルーコードと <code>.html</code> を出力します。</li>
<li>さらに <code>--shell-file html_template/shell_minimal.html</code> と指定しました — これは例を実行する HTML を生成するための、HTML テンプレートパスです。</li>
</ul>
</li>
<li>
<p>この例を実行してみましょう。上記のコマンドで hello2.html が生成されます。これは生成された wasm コードに対してロード、実行などを行うグルーコードを含むテンプレートと同じ内容を持ちます。ブラウザを開いて最後の例と同じ出力であることを確認してください。</p>
</li>
</ol>
<div class="note">
<p><strong>注</strong>: 例えば、 <code>emcc -o hello2.js hello2.c -O3 -s WASM=1</code> のように <code>-o</code> フラグにHTMLファイルの代わりに .js ファイルを渡すことで JavaScript で出力することを指定できます。そのあとに、スクラッチで独自のカスタム HTML を作ることができます。しかし、これはおすすめできません — Emscripten はメモリ割り当て、メモリリークやその他の問題を扱うための JavaScript グルーコードが多数必要になります。これは提供されたテンプレートにすでに含まれています。全てをあなた自身で書くよりも簡単に使用できます。していること全てに習熟してきたら、必要に応じて独自のカスタマイズバージョンを作りましょう。</p>
</div>
<h3 id="C_で定義されたカスタム関数を呼び出す">C で定義されたカスタム関数を呼び出す</h3>
<p>C で定義された関数があって、それを JavaScript から呼び出したい場合、Emscripten の <code>ccall()</code> 関数と <code>EMSCRIPTEN_KEEPALIVE</code> 宣言 (対象の関数をエクスポートする関数リストに加えるものです (<a href="https://kripken.github.io/emscripten-site/docs/getting_started/FAQ.html#why-do-functions-in-my-c-c-source-code-vanish-when-i-compile-to-javascript-and-or-i-get-no-functions-to-process">Why do functions in my C/C++ source code vanish when I compile to JavaScript, and/or I get No functions to process?</a> を参照してください)) を使用します。これがどのように働くか見てみましょう。</p>
<ol>
<li>
<p>はじめに、次のコードを <code>hello3.c</code> として新しいディレクトリに保存します:</p>
<pre class="brush: cpp notranslate">#include <stdio.h>
#include <emscripten/emscripten.h>
int main(int argc, char ** argv) {
printf("Hello World\n");
}
#ifdef __cplusplus
extern "C" {
#endif
void EMSCRIPTEN_KEEPALIVE myFunction(int argc, char ** argv) {
printf("MyFunction Called\n");
}
#ifdef __cplusplus
}
#endif</pre>
<p>デフォルトでは、Emscripten が生成したコードは常に <code>main()</code> を呼び出し、他のデッドコードは削除されます。関数名の前に <code>EMSCRIPTEN_KEEPALIVE</code> を置くことによって、これが起こらなくなります。また、<code>EMSCRIPTEN_KEEPALIVE</code> を使用するために <code>emscripten.h</code> をインポートする必要があります。</p>
<div class="note">
<p><strong>注</strong>: <code>#ifdef</code> ブロックを加えたことによって、C++ のコードからこの例をインクルードしようとしても動作するでしょう。 C と C++ の間でのマングリング規則によって、他の場合では壊れることもありますが、ここでは C++ を使用している場合に、外部の C の関数として扱うように設定しています。</p>
</div>
</li>
<li>
<p>便宜上、この新しいディレクトリに <code>html_template/shell_minimal.html</code> (もちろん、このファイルはあなたの実際の開発環境に置きます)を加えます。</p>
</li>
<li>
<p>さて、再びコンパイル手順を実行しましょう。あなたの最新のディレクトリの中 (そして、Emscripten コンパイラ環境の入っているターミナルウィンドウ) で、このように C のコードをコンパイルします (NO_EXIT_RUNTIMEオプションを付与してコンパイルする必要があることに注意してください。そうしない場合、 main() 関数が終了したときにランタイムもシャットダウンされてしまい、コンパイルされたコードが正しく呼ばれなくなる可能性があります - 例えば、atexitの呼び出しなどの適切なCのエミュレーションに必要です):</p>
<pre class="brush: bash notranslate">emcc -o hello3.html hello3.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html <code>-s NO_EXIT_RUNTIME=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']"</code></pre>
</li>
<li>
<p>例をブラウザでロードしたら、前と同じものが見られるでしょう。</p>
</li>
<li>
<p>JavaScript から新しい <code>myFunction()</code> 関数を呼び出す必要があります。まずは、 以下のような{{htmlelement("button")}} を最初の <code><script type='text/javascript'></code> タグの上に加えましょう。</p>
<pre class="brush: html notranslate"><button class="mybutton">Run myFunction</button></pre>
</li>
<li>
<p>そして、{{htmlelement("script")}} 要素内の最後に次のコードを追加します ( <code></script></code> タグの直前):</p>
<pre class="brush: js notranslate">document.querySelector('.mybutton').addEventListener('click', function(){
alert('check console');
var result = Module.ccall('myFunction', // name of C function
null, // return type
null, // argument types
null); // arguments
});</pre>
</li>
</ol>
<p>これはエクスポートされた関数をどのようにして <code>ccall()</code> を使用して呼び出すかを示しています。</p>
<h2 id="関連情報">関連情報</h2>
<ul>
<li><a href="http://emscripten.org/">emscripten.org</a> — Emscriptenとそれの多種多様なオプションについての詳細を確認してください。</li>
<li><a href="https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#calling-compiled-c-functions-from-javascript-using-ccall-cwrap">Calling compiled C functions from JavaScript using ccall/cwrap</a></li>
<li><a href="https://kripken.github.io/emscripten-site/docs/getting_started/FAQ.html#why-do-functions-in-my-c-c-source-code-vanish-when-i-compile-to-javascript-and-or-i-get-no-functions-to-process">Why do functions in my C/C++ source code vanish when I compile to JavaScript, and/or I get No functions to process?</a></li>
</ul>
|