[Из песочницы] Мини-обзор библиотек для Reflection в C++

В силу скромной информации на эту тему, в данной статье проведу небольшой обзор и сравнение найденных библиотек для 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 imagegithub.com/rpavlik/luabindСейчас для binding’а в Lua используется Luabind (rpavlik’s fork), но результирующую мета-информацию больше ни для чего особо не используешь.

Пример

luabind: module (state) [ luabind: class_(«Test») .def («func», &Test: func) ]; local obj = Test () obj: func (1, 2) Benchmark— Invoke — 1100ms— FindMetaMethod+Invoke — 1580ms

2) Camp imageprojects.tegesoft.com/pm/projects/campgithub.com/tegesoft/campСоздавалось французской компанием под вдохновением от luabind. Выглядит довольно культурно и проработано.Правда заметно не обновлялось уже года 4.

Пример

CAMP_TYPE (Test)

camp: Class: declare(«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() Benchmark (сбилдить не удалось, взял результаты с сайта другой библиотеки)— Invoke — 6889ms < — совсем грустно

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

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

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

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

Пример

cpgf: GDefineMetaClass :: 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(); Benchmark— Invoke — 1000ms— FindMetaMethod+Invoke — 1135ms < — быстрее, чем luabind

4) RTTR imagewww.axelmenzel.de/projects/coding/rttrАвтор, вроде, немец. Ура — C++11. Активно развивается, красивый синтаксис, современные возможности, очень даже радует. В ближайшее время должна появиться новая версия с существенным рефакторингом.

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

Пример

RTTR_DECLARE_STANDARD_TYPE_VARIANTS (Test);

RTTR_REGISTER { rttr: class_() .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(); Benchmark— Invoke — 1780ms— FindMetaMethod+Invoke — 2290ms

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

© Habrahabr.ru