Разработка на языке D – 13 лет спустя
13 лет назад я написал статью «Перспективы использования D в разработке игр» для журнала «FPS», в которой дал краткую характеристику основных особенностей языка и выделил главные его преимущества перед конкурентами. D изначально создавался как альтернатива C++ для разработки десктопных и серверных приложений – за прошедшие годы область применения языка не изменилась, хотя и добавились такие замечательные возможности, как поддержка ARM и Web Assembly. Но конъюнктура рынка и технологический статус кво сегодня уже несколько иные, поэтому я счел необходимым написать новую статью на ту же тему: насколько D актуален в геймдеве в 2023 году?
За прошедшее десятилетие сильно увеличился разрыв между тремя ключевыми потребительскими платформами – десктопом, мобильными устройствами и вебом (игровые консоли я в данном случае причисляю к десктопам). К сожалению, ни о какой унификации, ни о каких единых кодовых базах для компиляции приложений под все платформы пока говорить не приходится, несмотря на все усилия консорциумов по разработке платформонезависимых стандартов. OpenGL с его зоопарком версий по состоянию на конец 10-х годов так и не стал универсальным графическим интерфейсом – игровые движки все так же вынуждены поддерживать несколько бэкендов под разные API. Это привело к тому, что коммерческие игры в массе своей пишутся на готовых движках со встроенными кодогенераторами, высокоуровневой графикой и скриптовыми языками: Unity, Unreal Engine и Godot позволяют транслировать логику игры в самые разные целевые платформы, в том числе под мобильные ОС и HTML5 (такой же подход используется в языке Haxe).
При этом чем выше кроссплатформенность, тем больше прослойка между вашим приложением и системой – вплоть до полного отсутствия прямого доступа к ней; в результате приложение становится намертво привязанным к прослойке (vendor lock-in) – я до сих пор считаю это слишком высокой ценой за удобство и универсальность. Кроме того, прослойка всегда ориентирована на решение типовых задач – готовые движки заточены на создание игр популярных жанров и стандартных механик. Что-то уникальное с их помощью создать очень трудно, а зачастую невозможно. Я не могу себе представить разработку произведения демосцены на готовом движке – или, скажем, мало-мальски эффективного эмулятора игровой консоли. Для ширпотреба возможностей Unity и UE хватает, но все по-настоящему сложное и революционное пишется с нуля на кастомных движках – развитие графических технологий по-прежнему происходит на уровне абстракции нативных приложений, поэтому нам все еще нужны удобные компилируемые языки и хорошие тулчейны для них.
В геймдеве и низкоуровневом программировании сбросить C++ с его трона не удалось еще никому. Но так ли этот язык незаменим, как кажется? У меня создалось впечатление, что сложные игры и движки пишутся на C++ только из-за некоторой накопленной с годами инерции. Чем сложнее и критичнее код, тем меньше вероятность, что его станут переписывать на другой язык – это неоправданный с точки зрения бизнеса расход ресурсов. Если на рынке есть разработчики на C++, то нет причин не нанять их для поддержки существующей кодовой базы. Также важна интеграция с другими инструментами и библиотеками – если у вас все и вся под C++, то бессмысленно переходить на что-то другое. Но если проект создается с нуля и с минимумом критичной инфраструктуры, причин выбирать C++ с каждым годом все меньше – причин именно рациональных, а не надуманных.
Мы живем в эпоху бума новых компилируемых языков, таких как Rust и Go. На их фоне D кажется не самым популярным – и, как следствие, в качестве серьезного конкурента C++ его уже мало кто рассматривает. Но сам язык от этого хуже не становится. За прошедшие годы D обзавелся библиотеками и инструментами – при наличии замечательного плагина для VSCode сегодня уже никто не скажет, что для D нет IDE. Есть отличные привязки к OpenGL всех версий, SDL, SFML, bgfx, Lua и т.д. Но главные преимущества языка все-таки не в этом.
Для меня один из критичных недостатков C++ – платформозависимый тулчейн. Проект на C++ практически невозможно сделать полностью переносимым между разными средами разработки – поэтому появились сторонние системы сборки, генераторы проектных файлов наподобие CMake. Но CMake не решает все проблемы – например, не умеет разруливать внешние зависимости. Жутко неудобный GNU Autotools, линуксовые пакетные менеджеры, специфичные для каждого дистрибутива, NuGet, требующий Visual Studio и т. д. – все это костыли. Мало какой проект на C++ со сложной структурой зависимостей соберется с полоборота на всех платформах – а уж если код не под свежую VS, то вообще беда… В этом отношении D с его встроенным менеджером DUB – просто космическая технология. 2010-е стали периодом всеобщего перехода на DUB, формирования официального реестра пакетов – и теперь добавлять себе в проект чужие библиотеки стало не сложнее, чем в Python или Node.js. Аналога DUB для C++ просто не существует. Хотя, конечно, в этом отношении у D нет особых преимуществ перед тем же Rust – сегодня все уважающие себя новые языки имеют стандартные менеджеры зависимостей. Но возвращаться в унылый мир C++, где нет этой привычной уже фичи, мне как-то совершенно не хочется – я не понимаю, кого в 2023 году может устраивать такое положение дел.
Когда-то D ругали за одновременное сосуществование двух версий (D1 и D2) – у C++ сегодня версий еще больше, причем для компиляции кода на разных стандартах языка требуется передать компилятору соответствующий параметр. -std=c++17 и тому подобное – это просто фейспалм. Пока комитетчики ISO плодили все новые стандарты «плюсов» – 11, 14, 17, 20 – D2, тем временем, окончательно стабилизировался, превратившись в предсказуемый и надежный инструмент.
C++ – это некий аналог PHP в мире нативного программирования: в нем тонны легаси и куча способов делать одно и то же. Чего стоит хотя бы одновременная поддержка и заголовочных файлов, и модулей – еще один способ усложнить вам жизнь при компиляции чужих исходников! PHP держится на плаву благодаря огромному количеству готового кода, множеству популярных фреймворков типа WordPress – похоже, что у C++ та же судьба. Я ничего не могу сказать по поводу бизнес-логики, каких-то корпоративных и научно-инженерных областей применения – может быть, там C++ сложно чем-то заменить, но почему такая ситуация должна сохраняться в геймдеве, где все, что вам нужно – это прямой доступ к системным API и нескольким вспомогательным C-библиотекам?
Вывод: C++, к сожалению, в обозримом будущем никуда не денется, так как нужно поддерживать и обновлять уже написанные программы. Однако нет никаких причин не начинать новые проекты на современных языках: будет все больше проектов на Rust, Go, серверном JavaScript и т.д. На Rust сегодня пишутся вполне серьезные сложные штуки – я лично пользуюсь многими из них, например, wgpu-native, эмулятором DuckStation, кодировщиком Gifsky и др. А все утилиты для своих рабочих задач, где требуется скорость, я теперь пишу исключительно на D.