C++ към JavaScript?!?!?!?!
C++ към JavaScript.
Няколко думи за мен
- Борислав aka Боби aka iboB
- Предимно С++ програмист
- Предимно програмист на игри
- Занимавам се с open source
- Работя в Chobolabs
Малко общи приказки
- Интро лекция
- През погледа на предимно С++ програмист
- ... за съжаление?
- А вие какви сте?
Още не...
JavaScript
- Единственият език, който върви във всички браузъри
- Можем да изпълняваме само JS в браузъра, но можем да пишем на всеки език, стига да го компилираме до JS
- ...което не е новост
- 2006: Google Web Toolkit (GWT) - Java към JS
- 2007: pyjamas - Python към JS
- 2009: CoffeScript - ъъммм... CoffeScript към JS
- ... и други повече и по-малко незначителни боклици
asm.js
- Тъй като JS е много фьешън, авторите на браузъри искат да го оптимизират ...с хиляди човекочасове
- Още през 2000-те се заражда идеята за подмножество на JS, което работи добре с оптимизациите
And thus, asm js was born
Какво е asm.js?
- Не е асемблер!
- (Асемблер е WebAssembly, но за него друг път)
- Той е (work-in-progress) спецификация за използване на подмножество от JS
- Основни положения
- Всички integer променливи са силно типизирани
- Как? С type inferrence:
var|0
- Няма заделяне на памет
- Как? С typed array heap и views
- И някои яки браузъри като FireFox и IE имат специални оптимизации за него. Още те чакаме, Chrome
asm.js код
C функцията strlen
:
function strlen(ptr) {
ptr = ptr|0;
var curr = 0;
curr = ptr;
while (MEM8[curr]|0 != 0) {
curr = (curr + 1)|0;
}
return (curr - ptr)|0;
}
LLVM
- Това е компилаторна инфраструктура
- Front end
- Компилатор от даден език до Intermediate Form
- Такива има за C, C++, Fortran, Go, Rust и др.
- LLVM
- Превежда IF на оптмизиран гъзарски IF
- Back end
- Превежда IF до желан краен формат
- ... например binaries за дадена платформа
- И един back end превежда до asm.js...
... най-сетне
Emscripten
- Формално това е llvm back end към asm.js
- И... уж... би могло да работи за всеки език, който има front end за llvm
- ...но
- Езиците се нуждаят от рънтайм библитоека
- ...a, Emscripten дава такава само за С и С++
- Реално е С/С++ компилатор до asm.js
Бързодействието на JavaScript с удобството на C++. Kой не би искал това?
Наш пример
Getting started
- Инсталиране
- Компилиране на програми
- Абсолютно същото, което бихте направили, за да билдвате с clang
$ clang
=> $ emcc
- makefiles работят
- CMake работи
C/C++ погледнат през JS
Module
е нашият С/C++ свят, погледнат през JS
- Дава ни елементарен контрол върху програмата
- Позволява ни да зададем console I/O
- Command-line arguments
- Стартираме ли със зареждането на страницата и т.н.
Module.cwrap / Module.ccall
ни позволяват да викаме С функции от нашата програма
- Embind
- Boost.Python или luabind за JS през Emscripten
- Нещата показани през embind отиват в
Module
JS погледнат от С
- Наблягам на С, а не С++
emscripten.h
emscripten_run_script
- изпълнява стринг
EM_ASM...
- макроси за улеснено ползване
- Всяка глобална JS функция е достъпна в С
- JS exceptions директно прелитат през нашите C/C++ функции. Няма начин да ги видим
- И като става дума за това. Обратното също е вярно - C++ exceptions са невидими за JS
Константен хийп
- Ако си спомняте как работи asm.js...
- Размерът трябва да е ясен по време на компилиране (16 MB или
-s TOTAL_MEMORY=12345
)
-s ALLOW_MEMORY_GROWTH=1
- работи в крайни случаи, но е непрактично, бавно и не-asm.js-но
- Скрити любопитни факти за emscripten heap-а
- memory corruption на стероиди
- null pointer dereference не е грешка
- edge case, в който emscripten build е по-бърз от native build - много заделяния на памет
File I/O
- Като цяло, работи отлично с local storage
- Като цяло, обаче, ресурсите, които искаме да ползваме ги няма на local storage
- preload и embed - може би приемливи решения
emscripten_async_wget
- може би наложително решение
emscripten_wget
- може би неработещо решение
Directory I/O
- Всички С и С++ функции за работа с директории работят на 100% без проблеми
- За съжаление такива функции не съществуват
void ensure_path_exists(string path) { // Our sad solution to this problem
auto pos = path.rfind('/');
if (pos != string::npos) {
path.erase(pos);
if(path.empty()) return;
ensure_path_exists(path); // Recurse for parent directories
static string format =
"try {\n"
"var path = '%s';\n"
"FS.mkdir(path);\n"
"} catch(e) {}"; // Ignore exception that it exists
size_t len = format.length() + path.length();
char* js = new char[len];
sprintf(js, format.c_str(), path.c_str());
emscripten_run_script(js);
delete[] js;
}
}
Multi-threading
- Късият отговор е: Не
- Дългият отговор е: Еми-и-и... не съвсе-ем...
- Worker threads с
emscripten_call_worker
- 100% същите ограничения като worker през JS
- Хак през Worker threads с SharedArrayBuffer
- "Работи" и за pthreads и за
std::thread
- Не всеки браузър го поддържа (още)
- Единственото "споделено" нещо е С++ памет
- Съответно опити да правите друго в thread-a ще водят до краш
Силно платформени неща
html5.h
+ emscripten.h
- Дават ни emscripten-specific C++ интерфейс към повечето неща, които браузърът може да прави
- Достъп до елементи от страницата
- Вход от устройства като клавиатура и мишка
- Звуци
- HTTP заявки (с малко бъгове)
- И това е за платформата...
Ама мрън мрън мрън! Това значи, че за повечето мулти-платформени приложения, които искам да правя, трябва да добавя един тон emscripten-specific код?
-- анонимен писач на мулти-платформени приложения
За щастие, не, анонимни писачо. Някой вече го е направил.
Библиотеки
- Множество добри хора, са се усетили че това, което браузърът ни дава, много прилича на съществуващи мулти-платформени библиотеки
Да, аз мога да компилирам libpng с emscripten, но браузърът ми МОЖЕ да борави с png. Не може ли да се възползвам от това?
Да, аз мога да напиша WebGL рисуване за моята игра, но WebGL толкова прилича на OpenGL. Не може ли кодът да ми е един?
Enter Emscripten Ports
Emscripten ports
- Библиотеки, които официално имат поддръжка или версия специално за emscripten
- В момента това са:
OpenGL, SDL, SDL2, SDL2_image, SDL2_net, SDL2_mixer, SDL2_ttf, FreeType, Vorbis, Ogg, Bullet, zlib, libpng
Учудващо libjpeg, JsonCpp, curl, OpenSSL още не са в горния списък
Това значи, че, ако софтуерът ви ползва само от тези библиотеки, ще можете да го компилирате за emscripten (почти) без промени
Дебъгване
- Ако имате опит с TypeScript, CoffeeScript и подобни, знаете че браузърите поддържат source map файлове
- С компилационна настройка
-g4
, същото важи и тук
- За съжаление, обаче не можем да гледаме стойностите на променливите
Debugging demo