А подскажите какие-нибудь решения по рефлексии в С++.
Имеется: много классов (штук 300), они выстроены в иерархию от общего базового класса.
В каждом классе есть поля данных, разных типов:
— базовых (int, float...),
— строки,
— указатели на объекты других классов этой иерархии,
— указатели на некоторые внешние классы.
— массивы базовых типов
— массивы указателей на классы иерархии
— указатели на массивы указателей на классы иерархии
(проект не мой, я просто кое-что допиливаю)
Хочется сделать универсальный обход по объектам этой иерархии классов, как по дереву, с загрузкой данных в GUI дерево.
При этом поля данных должны остаться самими собой, то есть крайне нежелательно заворачивать их в какие-то объектные обертки с перегруженными операторами.
Нужно синтаксически обернуть эти поля данных в какие-то макросы, чтобы в итоге, кроме собственно полей данных, в классах сгенерировалась какая-то функция или метаданные или что-то еще (?)
со следующими свойствами
1. внешняя итерация по множеству полей класса, в общем без разницы какая — последовательная или с произвольным доступом по индексу.
2. через итерацию — обобщенный доступ к каждому элементу, с получением :
2.1 имени самой переменной (это понятно, через # препроцессора),
2.2 текстового значения переменной, если оно есть (для строк, чисел и типов, у которых определен какой-нибудь toString)
2.3 указателя на базовый класс иерархии, если это поле — указатель на любой класс иерархии, иначе null (это для рекурсивного обхода дерева)
3. если объект-массив, то можно его раскрыть рекурсивно как массив
в общем все это желательно совместить с описанием самих полей в классе. Сейчас мне приходится писать отдельные методы, которые возвращают нужную информацию для полей; хотя там все максимально автоматизировано на макросах — это все равно неудобно, можно забыть добавить поле в рефлексию или что-то перепутать.
Проблема именно в том, как в описании класса совместить собственно поля данных и что-то еще; если удастся совместить — остальное дело техники. Должно быть что-то типа такого
struct FOO : BASE
{
//... some code
REFL_F(int, x) // field
REFL_A(float, y, 3) // array
REFL_P(BAR*, ptr) // pointer to class hierarchy object
//... some code
};
Здравствуйте, x-code, Вы писали:
XC>Проблема именно в том, как в описании класса совместить собственно поля данных и что-то еще; если удастся совместить — остальное дело техники. Должно быть что-то типа такого XC>
struct FOO : BASE
XC>{
XC>//... some code
XC> REFL_F(int, x) // field
XC> REFL_A(float, y, 3) // array
XC> REFL_P(BAR*, ptr) // pointer to class hierarchy object
XC>//... some code
XC>};
Посмотри на Boost.Fusion и на макросы BOOST_FUSION_* (BOOST_FUSION_DEFINE_STRUCT и т.п.). В твоём случае можно сделать аналог таких макросов, тогда код получится вида:
Здравствуйте, Went, Вы писали:
XC>>А подскажите какие-нибудь решения по рефлексии в С++. W>А вам нужно чтобы рефлексия обозначалась именно в месте определения переменной? Или можно вынести это в отдельную функцию класса или вообще за оный?
В отдельную функцию делается элементарно, я сейчас так и делаю. Хочется именно в месте определения переменной.
Здравствуйте, x-code, Вы писали:
XC>В отдельную функцию делается элементарно, я сейчас так и делаю. Хочется именно в месте определения переменной. Если я правильно понял наши ограничения, то выход один: декларация переменной-члена указанным макросом должна вызывать параллельный код, который зарегистрирует данную переменную в неком хранилище.
Проблемы две:
1. Вытащить имя класса. Если не указать его каким-то вышестоящим макросом, то я даже не знаю как его вытащить иначе
2. Получить код, который будет выполнен для данной регистрации. Просто функцию дописать нельзя. Ее никто не вызовет. Значит нужна переменная, которая в своем конструкторе выполнит регистрацию члена. Если переменная статическая, то это хорошо, потому что код будет выполнен единожды. И переменная должна быть интегральной. Значит, мы можем определить эту переменную каким-то-мусором, а работу выполним в constexpr функции инициализации. И будем молиться, чтобы оптимизатор не выкинул.
struct X
{
DECL_STRUCT(X);
DECL_VAR(int, x);
};
развернем во что-то такое:
struct X
{
typedef X __ThisType;
int x;
static const int __x_registrator = register_member_var(&__ThisType::x);
};
Ну, а начинка register_member_var уже вроде как тривиальна.
Правка. Бред. constexpr-функция не имеет сторонних эффектов. Буду думать дальше.
Здравствуйте, LaptevVV, Вы писали:
LVV>А в Qt смотрели? Там вроде сделано...
Чере moc(Meta-Object Compiler), отдельную утилиту которая генерит метоинформацию на основе макросов Q_OBJECT и т.д., с определеными ограничениями.
Здравствуйте, x-code, Вы писали:
XC>Нужно синтаксически обернуть эти поля данных в какие-то макросы, чтобы в итоге, кроме собственно полей данных, в классах сгенерировалась какая-то функция или метаданные или что-то еще (?) XC>со следующими свойствами XC>1. внешняя итерация по множеству полей класса, в общем без разницы какая — последовательная или с произвольным доступом по индексу. XC>2. через итерацию — обобщенный доступ к каждому элементу, с получением : XC>2.1 имени самой переменной (это понятно, через # препроцессора), XC>2.2 текстового значения переменной, если оно есть (для строк, чисел и типов, у которых определен какой-нибудь toString) XC>2.3 указателя на базовый класс иерархии, если это поле — указатель на любой класс иерархии, иначе null (это для рекурсивного обхода дерева) XC>3. если объект-массив, то можно его раскрыть рекурсивно как массив
XC>в общем все это желательно совместить с описанием самих полей в классе. Сейчас мне приходится писать отдельные методы, которые возвращают нужную информацию для полей; хотя там все максимально автоматизировано на макросах — это все равно неудобно, можно забыть добавить поле в рефлексию или что-то перепутать.
XC>Проблема именно в том, как в описании класса совместить собственно поля данных и что-то еще; если удастся совместить — остальное дело техники. Должно быть что-то типа такого XC>
struct FOO : BASE
XC>{
XC>//... some code
XC> REFL_F(int, x) // field
XC> REFL_A(float, y, 3) // array
XC> REFL_P(BAR*, ptr) // pointer to class hierarchy object
XC>//... some code
XC>};
Если хотеть именно определение данных кучей макросов, то сразу вспоминается boost fusion (который выше уже посоветовали). А если посмотреть на список требований, то это сильно похоже на boost serialization — но там разделено определение полей и итерация по ним. А дальше дело вкуса, я обычно предпочитаю serialization.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, x-code, Вы писали:
XC>Нужно синтаксически обернуть эти поля данных в какие-то макросы, чтобы в итоге, кроме собственно полей данных, в классах сгенерировалась какая-то функция или метаданные или что-то еще
У меня такое есть на работе, это несколько килобайт макросов
Зато все супер-настраиваемое и плагинное
Расшарите не могу, сорри.
В качестве направления мысли — список полей представляет собой PP_SEQ, где каждое поле — это тоже PP_SEQ (тип, имя, дальше прочие вкусности, которые распознаются плагинами). Из это можно генерить что угодно, в приципе.
Здравствуйте, jazzer, Вы писали:
J>У меня такое есть на работе, это несколько килобайт макросов J>Зато все супер-настраиваемое и плагинное J>Расшарите не могу, сорри. J>В качестве направления мысли — список полей представляет собой PP_SEQ, где каждое поле — это тоже PP_SEQ (тип, имя, дальше прочие вкусности, которые распознаются плагинами). Из это можно генерить что угодно, в приципе.
Я делал такое на boost.preprocessor для перечислений. Хотя оказалось, что простые перечисления с метаинформацией (например именами элементов перечисления) дешевле и проще делать не через буст, а очень простым способом: специальными макросами, раскрывающимися или в перечисление, или в описание статического константного массива — в зависимости от макропеременной; и двойным подключением заголовочника с таким перечислением — один раз без макропеременной (использование по умолчанию — для всех мест), другой раз с определенной макропеременной (единственный раз — в специальный .cpp файл).
Можно конечно такое и с классами провернуть... но слишком много придется заворачивать в макросы, и в таких классах нельзя вставлять никакой код без макросов, иначе будет двойное определение. В этом смысле boost.preprocessor лучше, т.к. не требует двойного include.
Здравствуйте, x-code, Вы писали:
XC>Можно конечно такое и с классами провернуть... но слишком много придется заворачивать в макросы, и в таких классах нельзя вставлять никакой код без макросов, иначе будет двойное определение. В этом смысле boost.preprocessor лучше, т.к. не требует двойного include.
Вот именно boost.preprocessor я и пользуюсь — полет нормальный, код добавляю и сверху, и снизу, и даже наследование поддерживается.
А все потому, что синтаксически это выглядит так:
Суть в том, что не структура представляет самой вызов макроса, а макрос зовется внутри структуры. С таким синтаксисом нет проблем добавить что угодно хоть сверху, хоть снизу.
возьмите https://pypi.python.org/pypi/CppHeaderParser/ и напишите небольшой скрипт-кодогенератор на питоне. Это будет гораздо более поддерживаемо (при всём уважении к почтенным господам ) чем весь предложенный мрак на препроцессоре и шаблонах.