Инструменты11 сентября 2026 г., 09:46 МСК🤖 Auto

Arkane Studio: как запрет C++ создал Dishonored

Разбор внутренней архитектуры C++ от Arkane Studios: запрет исключений, RTTI и аллокаций ради предсказуемого рантайма и нулевых накладных расходов.

Баннер новости 7251

Контекст: Архитектурная дисциплина вместо стандарта

В 2016 году команда Arkane Studios (Остин) внедрила строгий подмножество C++14, получившее в индустрии неофициальное название «арканутый C++». Ключевая философия подхода — разделение ответственности: компиляционное время (compile-time) остается максимально свободным для метапрограммирования, тогда как рантайм (runtime) жестко ограничивается для обеспечения предсказуемости производительности. Главная цель — zero frame allocations (отсутствие аллокаций памяти во время отрисовки кадра) и фиксированная цена вызова функций.

Что запрещено, а что разрешено

Команда отказалась от универсальности языка в пользу контроля. Запрещены механизмы, добавляющие скрытое поведение или усложняющие анализ потока управления. Разрешены инструменты, работающие на этапе компиляции, что позволяет перенести проверки на стадию сборки.

Категория Статус в Arkane C++ Причина / Альтернатива
Исключения (try/catch) ❌ Запрещены Усложняют анализ потока управления; замена: явная проверка Result<T, E>
RTTI (dynamic_cast) ❌ Запрещены Скрытые накладные расходы; замена: статическая типизация и шаблоны
std::shared_ptr ❌ Запрещены в хотпасе Атомики и control block замедляют рендер; замена: value semantics или raw pointers с четким владением
if constexpr, std::span ✅ Разрешены Работают на compile-time, помогают проверять контракты до запуска
std::string_view ✅ Разрешены Избежание копирований строк без аллокаций

Модель обработки ошибок: Result vs Panic

Вместо исключений используется тип Result<T, E>, который делит ошибки на два уровня. Это позволяет явно контролировать, где ошибка является частью логики, а где — критическим сбоем.

  • Ожидаемые ошибки (Result): Файл не найден, разрыв соединения, исчерпание пула памяти. Вызывающий код получает ошибку и решает, как действовать дальше (например, повторить запрос или использовать данные по умолчанию).
  • Нарушение контракта (Panic): Выход за границы массива, разыменование null, глобальный OOM. В этих случаях вызывается единая функция panic(), которая пишет диагностику и аварийно завершает игру. Это предотвращает неопределенное поведение (UB) и упрощает отладку.

Для предотвращения игнорирования ошибок тип Result помечается атрибутом [[nodiscard]]. В проекте это было усилено: все предупреждения компилятора превращались в ошибки сборки, а попытка получить значение из Result без проверки приводила к panic() с выводом source_location.

Цена предсказуемости

Такой подход имеет свою цену. Из-за активного использования шаблонов и метапрограммирования время полной сборки движка и игры с нуля может достигать 4 часов. При изменении системных файлов рекомпиляция занимает около 15 минут. Однако это окупается стабильностью FPS и отсутствием «фризов» из-за сборщика мусора или аллокаций в критических секциях рендера.

Почему это важно

Пример Arkane показывает, что современный C++ — это не только новые фичи стандарта, но и архитектурные решения. Отказ от «удобных» абстракций (исключений, умных указателей) в пользу явного контроля позволяет создавать игры с высокой производительностью, где каждый байт и такт процессора на счету. Это пример того, как дисциплина кода может превзойти универсальность языка.

Источник: Habr ↗