Препроцессор C ++: избегать повторения кода в списке переменных-членов
======================================================================

У меня есть несколько классов, каждый с различными переменными-членами, которые тривиально инициализируются в конструкторе. Вот пример:

У каждого есть связанный`print<>()` функция.

Этот код подвержен ошибкам, поскольку список параметров реплицируется в четырех местах. Как я могу переписать код, чтобы избежать этого дублирования? Я хотел бы использовать препроцессор и / или шаблоны.

Например, могу ли я использовать технику препроцессора X-args - что-то вроде этого?

Или лучше шаблонный подход?

Пожалуйста, не задавайте вопросов, почему я хочу это сделать, существуют обоснованные решения по проектированию, которые привели к появлению нескольких похожих объектов с именованными параметрами. Параметры должны быть именованы переменными-членами для повышения производительности. Я просто изучаю, возможно ли перечислить параметры и их типы только один раз.

 чтобы препроцессор генерировал данные отражения о полях. Эти данные могут быть сохранены как вложенные классы.

Во-первых, чтобы было проще и понятнее записать его в препроцессоре, мы будем использовать типизированное выражение. Типизированное выражение - это просто выражение, которое помещает тип в круглые скобки. Так что вместо того, чтобы писать`int x` ты напишешь`(int) x`, Вот несколько полезных макросов, которые помогут с типизированными выражениями:

Далее мы определяем`REFLECTABLE` макрос для генерации данных о каждом поле (плюс само поле). Этот макрос будет называться так:

Итак, используя[Boost.PP](https://www.boost.org/doc/libs/1_49_0/libs/preprocessor/doc/index.html) мы перебираем каждый аргумент и генерируем данные следующим образом:

Что это делает, это генерирует константу`fields_n` это количество отражаемых полей в классе. Затем он специализируется на`field_data` для каждого поля. Это также друзья`reflector` класс, так что он может получить доступ к полям, даже если они являются частными:

Теперь, чтобы перебрать поля, мы используем шаблон посетителя. Мы создаем диапазон MPL от 0 до количества полей и получаем доступ к данным поля по этому индексу. Затем он передает данные поля предоставленному пользователем посетителю:

Теперь для момента истины мы собрали все это вместе. Вот как мы можем определить`Person` учебный класс:

Вот обобщенный`print_fields` функция:

Пример:

Какие выводы:

И вуаля, мы только что реализовали отражение в C ++, менее чем в 100 строках кода.



 пример, несколько похожий на ваш вариант использования.



`REFLECTABLE` Макрос Павла. Мне нужно было иметь пустой список полей, т.е.`REFLECTABLE()`, чтобы правильно обрабатывать иерархию наследования. Следующая модификация обрабатывает этот случай:



 что ваше решение оптимально для этого уменьшенного варианта использования. Мы можем помочь, если у вас есть дополнительные функции помимо`print` это выиграет от перебора полей.

Это идеальный пример для[Boost.Fusion](https://www.boost.org/doc/libs/1_50_0/libs/fusion/doc/html/index.html) [Последовательности слияния](https://www.boost.org/doc/libs/1_50_0/libs/fusion/doc/html/fusion/introduction.html); они могут быть использованы для представления отражения во время компиляции. Кроме того, вы можете генерировать более общее поведение во время выполнения.

Так, например, вы можете объявить свои элементы, используя Fusion.Map (который ограничивает вас одним вхождением каждого типа) или другие подобные фантазии.

Если ваш тип не соответствует Fusion Sequence (или вы не хотите вмешиваться в его внутренности), в адаптере есть адаптеры.[адаптированный](https://www.boost.org/doc/libs/1_50_0/libs/fusion/doc/html/fusion/adapted.html) раздел, такой как[`BOOST_FUSION_ADAPT_STRUCT`](https://www.boost.org/doc/libs/1_46_1/libs/fusion/doc/html/fusion/adapted/adapt_struct.html), И, конечно же, так как не все`struct` (или есть открытые члены), есть также более общая версия для классов, она скоро станет ужасной:[`BOOST_FUSION_ADAPT_ADT`](https://www.boost.org/doc/libs/1_50_0/libs/fusion/doc/html/fusion/adapted/adapt_adt.html).

Кража из[быстрый старт](https://www.boost.org/doc/libs/1_50_0/libs/fusion/doc/html/fusion/quick_start.html):

Адаптеры позволят вам «адаптировать» тип, так что вы получите:

А потом:

Это довольно внушительная библиотека :)



 а не класс. Это легко решит все ваши проблемы, не прибегая к хакерской обработке препроцессора.



Определить макрос: REFLECT (CLASS\_NAME, MEMBER\_SEQUENCE)
где MEMBER\_SEQUENCE (имя) (возраст) (другое) (...)

Пусть REFLECT расширится до чего-то похожего на:

Вы можете использовать BOOST\_PP\_SEQ\_FOREACH, чтобы расширить SEQ на посетителей.

Затем определите вашего посетителя печати:

[http://bytemaster.github.com/mace/group\_mace\_reflect\_\_typeinfo.html](https://bytemaster.github.com/mace/group__mace__reflect__typeinfo.html)

<https://github.com/bytemaster/mace/blob/master/libs/reflect/include/mace/reflect/reflect.hpp>