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
178
179
180
181
182
183
184
185
|
---
title: Компиляция кода C/C++ в WebAssembly
slug: WebAssembly/C_to_wasm
translation_of: WebAssembly/C_to_wasm
---
<div>{{WebAssemblySidebar}}</div>
<p class="summary">После того как вы написали код на C / C ++, вы можете скомпилировать его в WebAssembly, например, с помощью инструмента <a href="/en-US/docs/Mozilla/Projects/Emscripten">Emscripten</a>. Давайте посмотрим, как это работает.</p>
<h2 id="Подготовка_рабочей_среды_для_Emscripten">Подготовка рабочей среды для Emscripten</h2>
<p>Первым делом установим компоненты для дальнейшей работы.</p>
<h3 id="Необходимые_компоненты">Необходимые компоненты</h3>
<p>Установите Emscripten SDK и настройте рабочее окружение используя инструкции: <a href="https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html">https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html </a></p>
<h2 id="Компиляция_примера">Компиляция примера</h2>
<p>Когда рабочее окружение подготовлено, попробуем собрать пример кода на языке Си при помощи Emscripten. Вам доступно большое количество опций для настройки компиляции, но мы рассмотрим только два основных сценария компиляции с использованием Emscripten:</p>
<ul>
<li>Компиляция в wasm и создание HTML-страницы для запуска вашего кода, а также JavaScript кода, необходимого для работы wasm модуля в веб-среде.</li>
<li>Просто компиляция в wasm и создание JavaScript кода.</li>
</ul>
<p>Мы рассмотрим оба способа ниже.</p>
<h3 id="Создание_HTML_и_JavaScript">Создание HTML и JavaScript</h3>
<p>Это самый простой способ, который мы рассмотрим. С его помощью вы сможете использовать Emscripten для создания всего что нужно, чтобы ваш код работал в браузере как модуль WebAssembly.</p>
<ol>
<li>Нам понадобится простой пример для компиляции. Скопируйте следующий код программы на Си и сохраните его в файле <code>hello.c</code> в новой папке на вашем локальном диске:
<pre class="brush: cpp">#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello World\n");
}</pre>
</li>
<li>Теперь, используя терминал, перейдите в каталог, в котором находится ваш файл hello.c, и выполните следующую команду:
<pre class="brush: bash">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> — Указывает, что мы хотим, чтобы Emscripten сгенерировал HTML-страницу <code>hello.html</code> запускающую наш код, а также сам модуль wasm и код JavaScript который позволит использовать модуль в веб-среде.</li>
</ul>
<p>На этом этапе в вашем каталоге должны находится:</p>
<ul>
<li>Бинарный код модуля wasm (<code>hello.wasm</code>)</li>
<li>Файл JavaScript, содержащий код связывающий нативные функции Си и JavaScript/wasm (<code>hello.js</code>)</li>
<li>HTML-страница для загрузки, компиляции и инициализации wasm модуля, и отображающий его вывод в браузере (<code>hello.html</code>)</li>
</ul>
<h3 id="Запуск_вашего_примера">Запуск вашего примера</h3>
<p>Теперь, все что нужно чтобы запустить полученный <code>hello.html</code> в браузере, это поддержка WebAssembly. Он включен по умолчанию в Firefox 52+, Chrome 57+ и последних версиях Opera. Также вы можете использовать модули WebAssembly в Firefox 47+, включив флаг <code>javascript.options.wasm</code> в <em>about:config</em>, или в Chrome 51+ и Opera 38+ перейдя в <em>chrome://flags</em> и включив флаг <em>Experimental WebAssembly.</em></p>
<p>Если все работает как планировалось, вы должны увидеть надпись "Hello world" на открывшейся веб-странице и в JavaScript консоли вашего браузера. Поздравляем, вы только что скомпилировали программу на Си в WebAssembly и запустили ее в своем браузере!</p>
<div class="note">
<p><strong>Примечание</strong>: На самом деле, если просто открыть полученный <code>hello.html</code>, то ничего работать не будет. Подразумевается что все файлы находятся на веб-сервере и вы запускаете страницу через <code>localhost/hello.html</code>. Для этих целей можно использовать отладочный веб-сервер Emscripten. Чтобы его запустить, откройте терминал, перейдите в каталог, в котором находятся ваши файлы и выполните команду <code>emrun hello.html</code></p>
</div>
<h3 id="Использование_собственного_HTML_шаблона">Использование собственного HTML шаблона</h3>
<p>Вы можете использовать собственный шаблон HTML. Давайте посмотрим, как это сделать:</p>
<ol>
<li>
<p>Прежде всего, сохраните следующий код в файле hello2.c в новом каталоге:</p>
<pre class="brush: cpp">#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>Теперь, используя терминал, перейдите в ваш новый каталог и выполните следующую команду:</p>
<pre class="brush: bash">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 страницы запускающей этот пример.</li>
</ul>
</li>
<li>
<p>Теперь давайте запустим этот пример. Команда, указанная выше, сгенерирует файл <code>hello2.html</code>, который будет иметь тоже содержание что и шаблон, но с некоторым кодом, добавленным в процесс загрузки сгенерированного wasm, запускающим его и т.д. Откройте его в своем браузере, и вы увидите тот же результат, что и прошлом примере.</p>
</li>
</ol>
<div class="note">
<p><strong>Примечание</strong>: Вы можете указать компилятору создавать только JavaScript кода, без HTML, используя внутри флага <code>-o</code>, <code>.js</code> вместо <code>.html</code> для формата выходного файла, например <code>emcc -o hello2.js hello2.c -O3 -s WASM=1</code>. После этого вы должны создать свой собственный HTML файл с нуля. Однако так делать не рекомендуется — Emscripten требуется большое количество связывающего кода для обработки операций выделения памяти, утечек памяти и других проблем, которые уже включены в предоставляемый шаблон. Намного легче использовать уже готовое решение, чем создавать свои собственные версии самому.</p>
</div>
<h3 id="Вызов_пользовательской_функции_определенной_в_Си">Вызов пользовательской функции, определенной в Си</h3>
<p>Если у вас есть функция определенная в коде на Си, которую вы хотите по необходимости вызывать из JavaScript, то вы можете использовать для этого функцию <code>ccall()</code> из Emscripten, и объявление <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">Почему функции в моем коде исчезают после компиляции и/или я получаю сообщение «Нет функций для обработки»</a>). Давайте посмотрим, как это работает.</p>
<ol>
<li>
<p>Для начала сохраните следующий код в файле <code>hello3.c</code> в новом каталоге:</p>
<pre class="brush: cpp">#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.h</code> для использования <code>EMSCRIPTEN_KEEPALIVE</code>.</p>
<div class="note">
<p><strong>Примечание</strong>: Мы используем блоки <code>#ifdef</code> чтобы, пример оставался рабочим если вы попытаетесь использовать C++ код. Из за различия в правилах преобразования имен между Си и Си++, этот код может сломаться, но мы написали его так, что функция будет рассматриваться как функция Си даже если вы будете использовать Си++.</p>
</div>
</li>
<li>
<p>Теперь добавьте <code>html_template/shell_minimal.html</code> в ваш новый каталог, просто для удобства. В настоящем проекте стоит размещать его в специально определенный каталог.</p>
</li>
<li>
<p>Теперь снова займемся этапом компиляции. Внутри вашего последнего каталога, используя терминал, скомпилируйте ваш Си код следующей командой. (Обратите внимание что при компиляции обязательно нужно использовать опцию NO_EXIT_RUNTIME, иначе после выполнения функции <code>main()</code>, рабочий цикл будет завершен. Это приведет, например, к вызову функции atexits и дальше будет невозможно использовать наш скомпилированный код. Другими словами это необходимо для правильной эмуляции Си.)</p>
<pre class="brush: bash">emcc -o hello3.html hello3.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html -s NO_EXIT_RUNTIME=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall"]'</pre>
</li>
<li>
<p>Если вы снова запустите пример в своем браузере, вы увидите тоже самое что и до этого!</p>
</li>
<li>
<p>Теперь нам нужно вызвать нашу новую функцию <code>myFunction()</code> из JavaScript. Прежде всего, добавьте {{htmlelement("button")}} как показано ниже, чуть выше первого открывающего тега <code><script type='text/javascript'></code>.</p>
<pre class="brush: html"><button class="mybutton">Run myFunction</button></pre>
</li>
<li>
<p>Теперь добавьте следующий код в конце первого элемента {{htmlelement("script")}} (чуть выше закрывающего тега <code></script></code>):</p>
<pre class="brush: js">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>
<li><a href="https://research.mozilla.org/webassembly/">WebAssembly on Mozilla Research</a></li>
<li>
<p><a href="/en-US/docs/WebAssembly/existing_C_to_wasm">Compiling an Existing C Module to WebAssembly</a></p>
</li>
</ul>
|