Несколько лет назад на EclipseCon я представил демонстрацию приложения C++ с использованием libSDL2 и показал, как его создать с помощью CDT и запустить для нескольких платформ, моего рабочего стола, BeagleBone с QNX и, наконец, в веб-браузере с помощью Emscripten. Я использовал CMake для системы сборки, и это отлично сработало для первых двух, но Emscripten действительно боролся с идеей, что что-то еще управляет сборкой. Я, наконец, понял это, но осталось впечатление, что должен быть более простой способ создания приложений WebAssembly.
Недавно в версии 8 clang они сделали цель wasm первоклассным гражданином, доступным в стандартном дистрибутиве. Я решил посмотреть и нашел хотя бы один пример на github, который показал, как это сделать. Вот краткий обзор того, как начать. Имейте в виду, однако, что одним из аргументов является nostdlib, что означает, что это очень простой пример. Но это еще одна область, в которой Emscripten зашел слишком далеко. Подробнее об этом позже.
Для начала этот пример — довольно простой калькулятор Фибоначчи, довольно стандартный для WebAssembly. Вот файл C++.
#include “wasm.h” WASM_IMPORT void log(int i); WASM_EXPORT int fib(int i) { int res = i <= 1 ? i : fib(i — 1) + fib(i — 2); log(res); return res; }
Я хотел показать, как C++ вызывает обратно JavaScript, поэтому мы импортируем очень надуманный метод журнала. Сама функция Фибоначчи довольно проста. Я создал пару макросов в файле wasm.h для управления функциями маркировки как импорта или экспорта.
#define WASM_EXPORT __attribute__((visibility(“default”))) \ extern “C” #define WASM_IMPORT extern “C”
Поскольку я пишу на C++, я хочу убедиться, что компилятор не искажает имена, поэтому я объявляю их как extern «C». Как видите, экспорт также включает видимость символа, который по умолчанию скрыт в Makefile.
Я запускаю это с node.js, который поддерживает WebAssembly, по крайней мере, с версии 8, которая есть у меня в Linux. Идея состоит в том, чтобы выполнять некоторые из наиболее ресурсоемких задач на моем сервере узлов, используя wasm. Вот мой js-файл.
const fs = require(‘fs’) async function run() { const buf = fs.readFileSync(‘./fib.wasm’) return await WebAssembly.instantiate(buf, { ‘env’: { ‘log’: function(i) { console.log(`log: ${i}`) } } }) } run().then(res => { const { fib } = res.instance.exports console.log(fib(10)) })
Он просто загружает файл wasm и передает его в функцию журнала. Когда это завершено, я извлекаю свою функцию fib из экспорта и запускаю ее. Вы должны увидеть вывод функции журнала (больше раз, чем я ожидал, по крайней мере), затем результат 55.
Как и в большинстве вещей C++, магия на самом деле находится в Makefile.
CXX = $(HOME)/wasm/clang-8/bin/clang CXXFLAGS = \ -Wall \ --target=wasm32 \ -Os \ -flto \ -nostdlib \ -fvisibility=hidden \ -std=c++14 \ -ffunction-sections \ -fdata-sections LD = $(HOME)/wasm/clang-8/bin/wasm-ld LDFLAGS = \ --no-entry \ --strip-all \ --export-dynamic \ --initial-memory=131072 \ -error-limit=0 \ --lto-O3 \ -O3 \ --gc-sections fib.wasm: fib.o $(LD) $(LDFLAGS) -o $@ $<
Здесь много волшебных флажков, и я должен поблагодарить автора примера, на который я ссылался выше, за то, что он помог мне начать. Мне придется поиграть с ними, чтобы увидеть, что на самом деле необходимо. Ключевым моментом здесь является то, что это не Emscripten, а обычный clang 8, который я скачал с llvm.org. Стандартной библиотеки нет, так что не пытайтесь сделать printf. Вы пока немного предоставлены сами себе.
Но это своего рода вывод, который я сделал. Emscripten позволяет разработчикам C++ легко портировать свои приложения для работы в Интернете. Это не заставляет разработчика C++ думать как веб-разработчика. Мне было бы интересно, как бы это выглядело, если бы вам не передали те причудливые библиотеки, которые вы получаете с Emscripten, и действительно просто хотели создать веб-приложение, например игру, используя стандартные API-интерфейсы JavaScript, которые вы получаете с узлом или браузер. Я думаю, вы закончите тем, что будете писать программы, как разработчик Arduino, где у вас тоже нет printf…