DynaMix

Динамично композиране и мутиране на типове в С++

Автор Борислав Станимиров

Какво става тук?

  • Представяне на библиотека - DynaMix
  • Времето е недостатъчно за всичко
    • Повече "какво"
    • По-малко "защо"
    • ...но не нула "защо"
    • почти нула "как"

Какво трябва да знаете?

  • Поне малко С++
    • Няма да се занимаваме с имплементация
  • Обектно-ориентирано програмиране

DynaMix: Приложение

  • За какво служи?
  • Организация на сложни обекти
    • Ама наистина сложни
    • В различни подсистеми
  • Чрез позволяването на динамични типове

Какво ни дава?

  • Нов поглед върху полиморфизма
  • Какво представлява
    • Клас "торба" dynamix::object
    • Динамично се сглобява от компоненти, наречени миксини
  • "Аз знам какво значи Mixin и не е това"
  • DynaMix = Dynamic Mixins
  • Може да не се справя със защитаването ѝ, но...
  • История.
    • Masthead Studios и Захари Караджов. Идея. Интерфейс
    • Аз. Boost.Mixin. Пренаписване. Подобряване?
    • Epic Devs
    • Gameloft
    • Чао, Boost. DynaMix
  • Има 158☆ в GitHub
  • Не е просто използваема, но и използвана

Какво точно прави?

  • Няма прости примери
    • Или ще са твърде глупави
    • ...или твърде дълги

"Прост" пример

Основни функции

  • Композиране
    • Композиране vs Наследяване
    • Редовен проблем в ООП
    • Класически динамичен полиморфизъм
  • Модифициране (Мутация)
    • Динамичен динамичен полиморфизъм

Защо?

Реалните случаи са по-тежки: Обекти в игра

  • Таксономия срещу тагове
    • Йерархиите често не работят
    • В някои езици йерархиите винаги не работят
    • Блоговете ни научиха, че таговете са нещо яко
  • Отново - сложни обекти
    • Срещат се често в игрите
    • ...и не само игрите
    • Особено, където има подсистеми

Конкретен пример

...най-сетне

Композиране

  • flying_creature
  • two_legged_creature
  • monster_ai
  • animated_model
  • directx_rendering

Мутиране

  • Подменяме monster_ai с player_control и ставаме шофьор на дракон
  • Подменяме directx_rendering с opengl_rendering - имаме игра за Линукс
  • Махаме flying_creature - (двукрак) гущер
  • Добавяме fire_breather - става страшно

Подходи

Oppan C++ Style

  • Mножествено наследяване
  • CRTP Миксини
    • Като трейтовете в Scala, PHP или Self
    • ...или ролите в Perl 6
    • ...или наследяването в Eiffel
    • ...или миксините в Ruby
  • Mножествено наследяване
    • Няма лесен начин за комуникация
    • side cast
  • CRTP Миксини, трейтове и т.н. - комуникация
  • Комбинаторна експлозия от типове
  • Колекции в силно типизирани езици. Нямаме общ тип.
    • Виртуално множествено наследяване?
  • Ами мутацията?
    • Само Ruby е готин

Алтернатива: ECS

Entity-component system

(в случая некласическа полиморфна система)

  • Две думи за Класически ECS
  • Базов обект (entity) съдържа компоненти
  • Интерфейси към компоненти
  • Базов клас за компоненти

Entity


class game_entity
{
    control* m_control;
    physical_data* m_physical_data;
    rendering* rendering;
    vector<type*> m_types; // ??
    ...
};

game_entity dragon;
dragon.set_control(new monster_ai);
dragon.set_physical_data(new animated_model("dragon.model"));
dragon.add_type(new flying_creature);
...

dragon.set_ai(new player_control);
            

Component


class component
{
    entity* self;
};

class monster_ai : public control, public component
{
    void simulation_tick() override
    {
        ...
        self->get_physical_data()->move_to(enemy);
        self->get_physical_data()->animate("flying"); // ??
    }
};
            

Ползвайте ECS

  • Entity-component системите не са зло
  • Ползват се много. Особено в игрите
  • Лесни са за разбиране
  • В крайна сметка решават споменатите проблеми

Най сетне: DynaMix

Полиморфен entity-component-system на стероиди

DynaMix дракон


object dragon; // just an empty object

mutate(dragon)
    .add<flying_creature>().add<two_legged_creature>()
    .add<monster_ai>().add<animated_model>().add<directx_rendering>();

set_model(dragon, "dragon.model"); // handled by animated_model
...
mutate(dragon).remove<monster_ai>().add<player_control>();
                
  • Миксин тук = компонент в ECS
  • C++ няма extension методи :(
  • Няма нужда да предвиждаме всичко в базовия обект

DynaMix Миксин


class monster_ai
{
    void simulate_tick()
    {
        ...
        move_to(dm_this, enemy);
        animate(dm_this, "flying");
    }
};
                
  • Да. Не наследява нищо. Библиотеката не е интрузивна.
  • dm_this = self = обектът, от който миксинът е част

Не може да бъде!

  • Да. Не може. Трябва още малко
  • В някой хедър:
  • 
    DYNAMIX_DECLARE_MIXIN(monster_ai);
                        
  • Хедър? C++ sucks. Трябват хедъри
  • Това е достатъчно за мутации на обекти
  • В някой .cpp файл:
  • 
    DYNAMIX_DEFINE_MIXIN(monster_ai, simulate_tick_msg & get_info_msg);
                        
  • Месиджи? Методи? Smalltalk (тайна: едно и също са)

Ин / тер / ф / ей / с

  • Разбиване на класическите интерфейси
  • Месиджите в DynaMix. Как?
  • 
    DYNAMIX_MESSAGE_0(void, simulate_tick);
    DYNAMIX_MESSAGE_1(void, move_to, object*, target);
    DYNAMIX_MESSAGE_2(rt, foo, arg1_t, arg1, arg2_t, arg2);
                        
    
    DYNAMIX_DEFINE_MESSAGE(simulate_tick);
    DYNAMIX_DEFINE_MESSAGE(move_to);
    DYNAMIX_DEFINE_MESSAGE(foo);
                        
  • Mixin feature list
  • Функциите на обекта = обединението на миксините
  • Резултат. Истински decoupling

Други приятности

Мултикаст


DYNAMIX_MULTICAST_MESSAGE_1(void, get_info, ostream&, out);
...
get_info(obj, cout);
                
  • Повече от един клиент на месидж
  • Мултикаст комбинатори
  • get_rendering_elements
  • Приоритет
  • ... & foo_msg & priority(5, bar_msg));

Уникаст приоритет

  • Най добрият печели.
  • Скриване на миксини. Не точно както в Ruby

mutate(monster).add<berserk_ai>();
... // do berserk stuff
mutate(monster).remove<berserk_ai>();
... // do regular stuff
                

Плъгини

  • Пълна абстракция между интерфейс и имплементация
  • Плъгин динамична библиотека - няма проблем
  • Външно обогатяване на обекти
  • Пример: Архитектурна CAD система
    • Плъгин за водопроводни/електриджийски екстри
  • Потребителски алокатори
  • Thread safety
  • Debugging визуализатори за Visual Studio
  • Бъдеще
    • Reflection. Интеграция със скриптови езици
    • Open Source игрица
    • Roadmap

Неприятности

  • Освен тези, които видяхме...
  • Харчи малко повече памет - пренебрежимо (?)
    • Допълнителна памет на уникален тип
    • Допълнителна памет за обект
  • Message call overhead - пренебрежим (?)
    • Малко по-бавно от виртуална функция.
    • Основният проблем е cache-locality

Кога е DynaMix e добра идея?

  • Когато имате сложни обекти - поне ECS
  • Когато имате подсистеми
  • Когато имате плъгини

Do you want to know more?

РТФМ @ ibob.github.io/dynamix/


Документацията не е съвсем пълна :(

Край

Въпроси?


От Борислав Станимиров / ibob.github.io

DynaMix е тук: github.com/iboB/dynamix


Презентацията е лицензирана с Creative Commons Признание 3.0
Creative Commons License