Несколько лет назад на 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…