Необычный паттерн: объект-самоубийца

В D, как известно, нет встроенного способа удалить объект – то есть, освободить занятую им память. Функция destroy лишь вызывает деструктор и помечает объект как недействительный, но фактически память высвобождается в следующем цикле сборки мусора. dlib, будучи библиотекой для разработки приложений реального времени, предоставляет альтернативные механизмы управления памятью с возможностью удалять объекты вручную – в моменты, явно определяемые программистом, а не логикой сборщика мусора. Это накладывает на программиста определенную степень ответственности, так как стопроцентно ручное управление памятью – занятие довольно хардкорное. Я написал на Medium статью на эту тему, где описал парадигму владения (ownership), рекомендуемую при работе с dlib. Суть ее в том, что удаление данных автоматически выполняет объект-владелец этих данных, когда кто-то – вы сами или его собственный владелец – удаляет его самого. Таким образом, вы у себя в коде расставляете единичные функции Delete только в ключевых местах, когда ваше приложение переходит из одного режима в другой, а вся рутинная работа по удалению данных ложится на иерархию объектов-владельцев. Например, если это игра, то вы можете удалить текущую сцену, когда пользователь завершает уровнень, проигрывает, выходит в главное меню или загружает сохранение. Если объект сцены является владельцем всех ее данных, то они будут автоматически удалены.

Но при этом может возникнуть неожиданная проблема. Допустим, у вас есть некий глобальный менеджер игры, который каждый раз передает управление загруженной сцене. Сцена формирует нужные ей структуры данных, обрабатывает входящие события, реагирует на пользовательский ввод, обновляет изменяемое состояние и рендерит графику – то есть, совершает довольно много задач в цикле, полагаясь на то, что все ее данные находятся в памяти. Если вам нужно завершить работу сцены, то это равносильно удалению объектом самого себя – то есть, сцена обращается к корневому менеджеру с запросом о переключении в другой режим, и он ее удаляет. Можно ли в dlib так делать?

(далее…)

DGL без сборки мусора

На данный момент на D, к сожалению, нет полноценных игровых движков с ручным управлением памятью. И, если в общем случае с производительностью мы ничего сделать не можем (Benjamin Thaut, чтобы оптимизировать производительность игровых приложений, использовал модифицированный рантайм языка), то пусть хотя бы управление ресурсами будет детерминированным.
Новая ветка проекта DGLgc-free – является попыткой достичь этого путем полного отказа от выделения динамической памяти через сборщик мусора. Вместо этого все классы  используют dlib.core.memory и dlib.container.array вместо оператора new и встроенных динамических массивов D.
В данный момент переписана примерно половина функциональности DGL, добавлено и много нового: например, новая система событий, поддержка многоязыкового ввода с клавиатуры и т.д.

dlib 0.5

Не так давно состоялось очередное крупное обновление коллекции библиотек dlib – вышла версия 0.5, наиболее значительным нововведением которой стала поддержка ручного управления памятью (РУП). Но – обо всем по порядку…

  • Новый модуль dlib.core.memory предоставляет средства для ручного выделения и высвобождения динамической памяти, независимые от сборщика мусора и основанные на malloc/free. Имеется поддержка структур, классов и массивов. При использовании классов рекомендуется использовать интерфейс ManuallyAllocatable и перегружать метод free, который ответственен за удаление объекта – в противном случае корректное удаление в некоторых случаях не гарантировано (например, при доступе через интерфейс или родительский класс).
  • Началась работа по переводу всей dlib на РУП. Так, загрузчики изрбражений (PNG, JPEG, TGA, BMP) в новой версии полностью независимы от сборщика мусора. Для этого активно используется паттерн абстрактной фабрики, ответственный за создание изображений  в памяти. Кстати, в загрузчике PNG значительно улучшена поддержка индексированных изображений, для них добавлена поддержка альфа-канала.
  • Кроме того, на РУП переведены некоторые контейнеры из dlib.container – BST, ассоциативный массив. Реализован полностью ручной динамический массив (dlib.container.array).
  • Еще одна новинка – ООП для структур (dlib.core.oop). Это экспериментальный модуль, реализующий для структур прототипный стиль ООП с поддержкой множественного наследования и параметрического полиморфизма. Полностью заменить классы он, конечно, не может, но окажется весьма полезен, если нужно создавать объекты с наследованием в стеке. В будущем планируется переписать некоторые внутренние механизмы dlib с использованием этой легковесной объектной системы.
  • В пакете dlib.math появилась поддержка дуальных кватернионов. Это частный случай алгербы Клиффорда, обобщение кватернионов на поле дуальных чисел. Их можно использовать, например, для описания движения тел в кинематике – один дуальный кватернион охватывает и перенос, и вращение. Кстати, реализация обычных кватернионов через инкапсуляцию теперь совместима с векторами.
  • Изменения коснулись и пакета вычислительной геометрии. Усеченная пирамида (dlib.geometry.frustum) теперь задается с нормалями ограничивающих плоскостей, указывающими наружу пирамиды. Подвергся изменению API проверки пересечения Frustum с AABB. Исправлены ошибки в реализации AABB и плоскости.