C++ reflection с блэк-джеком и шлюхами

Отсутствие Reflection в C++ порождает множество проблем, лично у меня, и возможно у многих людей, которые будут читать данный текст.

Первая проблема в том, что все функции оперирующие или выдающие как результат множества объектов должны работать с базовым типом, каким нибудь IEntity. Для примера: выбрали мы из мира с помощю AABB набор игровых объектов и хотим нанести урон, всем кто поддерживает интерфейс IDamageable, для этого обычно используют dynamic_cast или какой нибудь getType. На консолях, где RTTI в релизном билде обычно запрещен - приходится изобретать свои методы (см. библиотеку от Insomniac).

Вторая проблема, отсутсвие в С++ Property и средств для работы с ними. Движки по типу Unreal Engine3 и CryEngine приучают творческих людей работать с набором параметров у любого игрового объекта - будь то объект миссии, текстура или спецэффект, что совершенно логично и удобно.

Но вот незадача, в C++ отсутсвует возможноть получить какие либо метаданные класса - что бы узнать его поля / свойства и использовать это для управления объектом в реальном времени. Т.е. написание удобного и визуального средства редактирования C++ классов становится проблемой.

Существует множество вариантов решения, кто-то пишет уникальные редакторы для уникальных объектов, кто-то использует кодогенерацию мета-информации и классов, кто-то просто игнорирует проблему или пишет редактируемые объекты на скриптовых языках с поддержкой Reflection.

Третья проблема, система автоматического save / load любых объектов. Делают её обычно банально через virtual ::save(file * f) и virtual ::load (file *f) для всех объектов которым нужно уметь сохраняться, и с разной степенью успеха борются с проблемой при загрузке - как, имея имя класса в const char * создать экземпляр класса.

Чтобы решить эти проблемы один раз и навсегда, я написал маленькую библиотеку С++ reflection (скачать тут) которая позволяет удобно добавлять произвольную метаинформацию к любым классам, не требует специального препроцессора или кодогенерации и намного быстрее существующих средств в языке (dynamic_cast) или библиотек.

Вот пример объявления метаданных внутри cpp файла.

BEGIN_REFLECTION_METADATA(foo_class, base_class)
    ATTRIBUTE_FLOAT("myFloatProperty", &foo_class::setFloatFunc, &foo_class::getFloatFunc);
    ATTRIBUTE_INT("myIntProperty", &foo_class::setIntFunc, &foo_class::getIntFunc);
END_REFLECTION_METADATA(foo)
_Winnie C++ Colorizer

Немного макрос-магии и метаданные готовы. Для скорости, все строковые параметры используются вместе с хешами, вычисляемыми на этапе компиляции. Это дает реальный выигрыш в скорости по сравнению с dynamic_cast или альтернативами.

Пример использования dynamic_cast

base * base_ptr = new foo();
foo * foo_ptr = base_ptr->DynamicCast<foo>();
_Winnie C++ Colorizer

Плюсы и минусы библиотеки:
+ можно создавать классы по текстовому имени класса
+ получать список всех классов с метаинформацией и создавать их по метаинформации
+ получать список аттрибутов (property) для любого класса
+ быстрый dynamic_cast
+ получать метаинформацию из созданного экземпляра класса
+ аттрибуты поддерживают наследование
+ вся информация строится в compile time

- нужно писать небольшие макросы
- не поддерживается множественное наследование (для меня это фича)
- незначительно увеличивается время компиляции (нужно все таки хеши считать)

Вот несколько примеров скорости работы:

"Xenon X360 (3.2 GHz)"
-------------------
dynamic_cast 9.52M casts per second
reflection::cast 18.94M casts per second

прирост быстродействия x 1.99

"AMD 64X2 3800+ (2.0GHz)"
-------------------
dynamic_cast 12.02M casts per second
reflection::cast 35.50M casts per second

прирост быстродействия x 2.95

"Core2Duo E6750 (2.66GHz)"
-------------------
dynamic_cast 26.49M casts per second
reflection::cast 45.70M casts per second

прирост быстродействия x 1.73
_Winnie C++ Colorizer

Буду рад мнениям и результатам тестов - скомпилированный тест производительности лежит внутри архива. Повторяю ссылку, что бы не искать её выше по тексту. (Библиотека reflection)

UPDATE: Обновил архив, ускорил получение метаданных из указателя на объект.

2 Comments

JokerJanuary 15th, 2010 at 16:19

обновление: нужно убрать CRT strcmp из сравнения, на интелах он тормозит

RS_INLINE bool operator == (const reflection::staticString & s1, const reflection::staticString & s2)
{
int size1 = s1.size();
int size2 = s2.size();

if ( (size1 == size2) &&
(s1.hash() == s2.hash()))
{
const char * __restrict str1 = s1.c_str();
const char * __restrict str2 = s2.c_str();

for (int n = 0; n < size2; n++, str1++, str2++)
{
if (*str1 != *str2)
{
return false;
}
}

return true;
}

return false;
}

capibaraMarch 13th, 2010 at 19:01

да здравствует велосипеды! qt4 рулит.

Leave a comment

Your comment