Доброго времени суток, уважаемые пользователи Хабра.
Я не то что бы профессиональный разработчик на C++, в основном я занимаюсь геймдевом на UE5 (по крайней мере последнее время). Но последнее время достаточно часто я балуюсь разработкой десктоп приложений под Windows. Для красоты и простоты я задумывался об использовании именно react. Но из выбора что я увидел было 2 варианта:
-
Tauri — Фреймворк под Rust с отрисовкой фронтенда сделанном на React и т.п.
-
Electron — фреймворк для разработки кроссплатформенных настольных приложений с использованием веб-технологий (скопировал описание с гугла)
Electron я отмел сразу, так как хотел писать бэкенд на чем то более удобном чем JS/TS для себя. Tauri мне очень понравился, но изучение Rust заняло немного времени. И смотря на это все я подумал, что можно реализовать какой-нибудь аналог Tauri используя C++.
Немного посидев поизучав информацию, я примерно выстроил себе план работы, из этого вылился первый MVP Shine. Фреймворк на данный момент поддерживает только Windows и тесно связан с vcpkg, так как находится на очень ранней стадии разработки, но его уже вполне себе можно потыкать и использовать.
А теперь к установке и использовании:
Для начала то, что нам нужно для запуска:
-
CMake
-
Node.JS
-
CLion (можете использовать что-то своё, мне комфортнее в нём)
Дальше мы открываем терминал и прописываем команду:npm create shine-app@latest
У нас появится простая на данный момент настройка проекта, а именно указание его имени:
Дальше после того как мы введём имя приложение он создаст папку со всем, что нам нужно для реализации приложения
Структура проекта у нас выглядит следующим образом:
D:.├───cmake # кастомные cmake функции для удобства сборки и запуска├───frontend # Тут у нас находится фронтенд, сам UI приложения│ ├───public│ └───src│ └───assets├───generated # Тут у нас лежат ассеты собранные в бинарник, чтобы не таскать их за собой в релизе├───scripts # Скрипты сборки для удобства├───shine # Код библиотеки│ ├───components│ │ ├───include│ │ │ └───shine│ │ │ └───components│ │ └───src│ └───core│ ├───include│ │ └───shine│ │ └───engine│ └───src│ └───engine│ └───win32└───src # Код нашего приложения (в данном случае main.cpp)
На данный момент я не придумал ничего лучше, чем тянуть за собой код библиотеки в пример, чтобы избежать бед со сборкой с разными компиляторами
Дальше мы открываем проект в CLion (или там где удобно вам) и настраиваем профили CMake, У меня это выглядит так:
Дальше идем в View -> Tool Windows -> vcpkg
Далее выбираем наш существующий манифест, и нажимаем на карандашик сверху чтобы он работал с нашими CMake профилями:
Кликаем галочку Add vcpkg integration to existing CMake profiles
Далее кликаем ПКМ по корневому CMakeLists.txt и нажимаем Reload CMake Project.
Дальше мы можем спокойно запустить приложение и увидеть что оно работает:
Идею темплейта я решил взять просто с Tauri, простое окно где можно ввести имя и при нажатии Greet наше C++ ядро отформатирует сообщение и выведет его во фронтенде:
Со стороны C++ наш handler функции greet выглядит следующим образом:
SHINE_COMMAND(greet) { // У функции собранной с SHINE_COMMAND по дефолту есть аргументы // В развернутом виде функция выглядит примерно так: // nlohmann::json greet(const nlohmann::json& args) std::string name_str = args["name"]; return std::format("Hello, {}", name_str);}
и указание того, что мы можем вызвать эту функцию из фронтенда:
app.GetRouter().AddHandlers({SHINE_HANDLER(greet)});
После этого наш фронтенд знает что мы можем вызывать функцию greet с помощью метода invoke. Выглядит это следующим образом:
async function handleGreet() { if (!name.trim()) return; setIsLoading(true); try { // Вызываем функцию и записываем ответ в setGreetMsg const res = await invoke('greet', { name }); setGreetMsg(res.result || res); } catch (error) { setGreetMsg("Error: Could not connect to Shine Core"); } finally { setIsLoading(false); } }
Дальше мы можем как угодно переделывать наш проект React и делать наше UI. При сборке в дебаге приложение просто слушает локалхост с нашим портом, в Shine есть поддержка HMR и при изменении кода, UI в приложении так же изменится. При релизе же, проект фронтенда собирается и после чего переводятся в байты, дальше мы используем эти байты внутри приложения не распаковывая их храним в памяти.
Помимо всего этого у нас есть конфиг файл: shine.conf.json, выглядит он так по стандарту:
{ "window": { "title": "Shine Secure App", "width": 900, "height": 600 }, "capabilities": { "allowedCommands": [ "fs_read_text_file", "window_drag" ] }}
В нем пока что мы указываем title окна приложения, его размеры. Помимо этого есть ещё 2 параметра конфига:
-
frameless — Отключает рамки у окна
-
resizable — Разрешает / запрещает изменение размера окна
При дебаге конфиг будет подтягиваться из файла, при сборке он так же находится в итоговом приложении, опять же чтобы не тянуть за собой лишние файлы.
Учитывая что мы пишем на C++, и запихиваем ассеты фронтенда прямиком в бинарник, приложение из template имеет достаточно приятный вес — всего 845кб, при этом он не требует зависимостей для запуска и можно отправлять голый .exe пользователю и он запустится на его системе.
В планах у меня реализовать поддержку Linux и MacOS, а так же возможность получать состояние элементов фронтенда из плюсов и взаимодействия с ними. Например заполнять прогресс бар условный из C++ при какой-нибудь загрузке и т.п.
Это моя первая попытка сделать что-либо полезное на C++ для опенсорса, буду очень рад объективной критике, советам и контрибьютингу.
ссылка на оригинал статьи https://habr.com/ru/articles/1031200/