Мини-обзор библиотек для Reflection в C++

Мини-обзор библиотек для Reflection в C++

352
ПОДЕЛИТЬСЯ

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

Благодаря reflection можно:
— Просто создавать редакторы, в том числе интерфейсов, так как есть удачный доступ к мета-инфы о всех свойствах ваших объектов;
— Добавить binding для почти всех скриптовых языков сходу (Lua, Python, JavaScript и т.д.);
— Применять мета-информацию для автоматической сериализаци;
— Применять как фабрику объектов, создавая нужные экземпляры, имея только строчку с именованием тип;
— Применять в качестве наиболее легковесной подмены dynamic_cast;
— И прочее прочее прочее, в зависимости от фантазии и потребностей.

Далее идет обзор каждой библиотеки по очереди в силу моих умеренных способностей. Для каждой:
— короткое описание;
— пример binding’а и использования для такового класса:

class Test
{
public:
int func(int a, int b)
{
return a + b;
}
};
— результаты замера производительности на i5 3570K, Windows 8, Visual Studio 2013, Release x86 (замерялись раздельно 10 000 000 вызовов способа класса и раздельно 10 000 000 поиск метаметода+вызов).

Рассматривались лишь библиотеки, не требубщие доп шагов построения и инструментов (вроде qt moc и gccxml).

Библиотеки перечислены в порядке возрастания личного энтузиазма к ним.

1) Luabind

github.com/rpavlik/luabind

На данный момент для binding’а в Lua употребляется Luabind (rpavlik’s fork), но результирующую мета-информацию больше ни для чего же особо не используешь.

Пример
luabind::module(state)
[
luabind::class_<Test>("Test")
.def("func", &Test::func)
];

local obj = Test()
obj:func(1, 2)

Benchmark
— Invoke — 1100ms
— FindMetaMethod+Invoke — 1580ms

2) Camp

projects.tegesoft.com/pm/projects/camp
github.com/tegesoft/camp

Создавалось французской компанием под вдохновением от luabind. Смотрится достаточно культурно и проработано.
Правда приметно не обновлялось уже года 4.

Пример
CAMP_TYPE(Test)

camp::Class::declare<Test>("Test")
.function("func", &Test::func);

Test obj;
camp::Class t = camp::classByName("Test");
camp::Function m = t->function("func");
camp::Value v = m->call(obj, camp::Args(1, 2));
auto result = v.to<int>()

Benchmark (сбилдить не удалось, взял результаты с веб-сайта иной библиотеки)
— Invoke — 6889ms < — совершенно обидно

3) cpgf

www.cpgf.org/document/index.html
github.com/cpgf/cpgf

Смотрится проработано, но интерфейс достаточно усложнен и смотрится код совершенно не лаконично. Основной создатель вроде китаец. На простом примере не видно, но раз поглядеть tutorial, то становится сильно приметно — github.com/cpgf/cpgf/blob/develop/samples/tutorials/a01_reflect_to_global.cpp Много приставок, добавок в именовании, разных интерфейсов, правил использования (к примеру, как и когда передается владение).
При этом, все сведено к одному интерфейсу, что естественно веселит.

Большой плюс — отменная документация.

Из доп наворотов — сериализация, готовые решения для биндинга в Lua/JavaScript/Python, tweening, своя система событий.

Багофиксы были еще в декабре, то есть проект не мертв.

Пример
cpgf::GDefineMetaClass<Test>
::define("Test")
._method("func", &CpgfTest::func);

Test obj;
cpgf::GMetaClass* t = cpgf::findMetaClass("Test");
cpgf::GMetaMethod* m = t->getMethod("func");
cpgf::GVariant v = m->invoke(&obj, 1, 2);
auto result = cpgf::fromVariant<int>();

Benchmark
— Invoke — 1000ms
— FindMetaMethod+Invoke — 1135ms < — скорее, чем luabind

4) RTTR

www.axelmenzel.de/projects/coding/rttr

Создатель, вроде, германец. В наиблежайшее время обязана показаться новенькая версия с значимым рефакторингом. Ура — C++11. Активно развивается, прекрасный синтаксис, современные способности, чрезвычайно даже веселит.

Но пока, судя по коду, много для чего же решение достаточно прямолинейное, отсюда и существенно худшие характеристики производительности.

Пример
RTTR_DECLARE_STANDARD_TYPE_VARIANTS(Test);

RTTR_REGISTER
{
rttr::class_<Test>()
.method("func", &Test::func);
}

Test obj;
rttr::type t = type::get("Test");
rttr::method m = t.get_method("func");
rttr::variant v = m.invoke_variadic(obj, {1, 2});
auto result = v.get_value<int>();

Benchmark
— Invoke — 1780ms
— FindMetaMethod+Invoke — 2290ms

5) uMOF

bitbucket.org/occash/umof

Активно развивается. Создатель русскоговорящий, активно отвечает на все вопросцы. Создавалось, как я сообразил, под огромным впечатлением от QT. В наиблежайшее время обязана показаться новенькая версия с значимым рефакторингом и ускорением, она и тестировалась. Опять ура — C++11 (все эти constexpr и остальные радости).

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

Пример
U_DECLARE_API(Test, METHODS);
U_DECLARE_METHODS(Test)
{
U_METHOD(func)
};

const Api* api = U_API(Test)::api();
Method m = api->method(api->indexOfMethod("func(int,int)"));
int result;
m.invoke(&obj, result, {1, 2}));

Benchmark
— Invoke — 115ms < — мистика (в старенькой версии 420, что тоже на голову выше остальных)
— FindMetaMethod+Invoke — 1780ms < — уже не так отлично, но быстрее всего и это будет оптимизировано

Invoke практически в 9 раз скорее самого лучшего результата остальных библиотек.

Статься с графиками и картинами для еще старенькой версии тут — www.gamedev.net/page/resources/_/technical/general-programming/implementing-a-meta-system-in-c-r3905 Про это писал сам создатель, сравнивая свое решение с иными.
Там же есть сопоставление того, как какие библиотеки влияют на время компиляции и линковки проекта и как утяжеляют бинарник.

Общий итог
Luabind
Camp
cpgf
RTTR
uMOF
Invoke
1100
6889
1000
1780
115
FindMetaMethod+Invoke
1580
x
1135
2290
1780

Заключение
Самые современные, лаконичные и живые библиотеки — uMOF, RTTR.
Более богатая по функциональности — cpgf.
Выдающиеся по производительности:
— uMOF (благодаря особенностям реализации, неописуемо стремительный invoke и малый overhead в компиляции и размере бинарника);
— cpgf (на данный момент самый стремительный итог по FindMetaMethod+Invoke, что и является более частым сценарием использования).

Предложения для обсуждения
1) Какую все-таки разрабу игр выбрать библиотеку?
То есть, раз на каждом кадре делается порядка 700 вызовов, то при 60 FPS это составит меньше 1% от времени кадра. Может дать предпочтение, к примеру, наиболее современному RTTR, т.к. cpgf солидно проработан и указывает отличные результаты, но так ли важен этот overhead для invoke? 2290ms на 10000000 вызовов это 4366812 вызовов в секунду. 72780 вызовов на каждом кадре при 60 FPS.
При этом uMOF указывает выдающиеся результаты, что позволило бы применять его с наибольшей интенсивностью (что и планируется). Но он еще не закончен, не хватает некого функционала.
2) Может быть, какая-то библиотека пропущена? Можно было бы добавить ее в обзор.
Тут обзор был поверхностный, может ваш опыт говорит о каких-то существенных особенностях в пользу либо против какой-то библиотеки. 3) Что вы понимаете из собственного опыта про всякую из этих библиотек?

Заблаговременно огромное спасибо за ваши комменты. habrahabr.ru