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

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

struct Person { Person(const char *name, int age) : name(name), age(age) { } private: const char *name; int age; }; 

Каждая из них имеет связанную функцию print() .

 template  void print(const Person &person) { std::cout << "name=" << name << "\n"; std::cout << "age=" << age << "\n"; } 

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

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

 #define ARGUMENTS \ ARG(const char *, name) \ ARG(int, age) struct Person { Person(LIST_TYPE_NAME_COMMA(ARGUMENTS)) : LIST_NAME_INIT(ARGUMENTS) { } private: LIST_TYPE_NAME_SEMICOLON(ARGUMENTS) }; template  void print(const Person &person) { LIST_COUT_LINE(ARGUMENTS) } #undef ARGUMENTS 

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

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

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

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

 #define REM(...) __VA_ARGS__ #define EAT(...) // Retrieve the type #define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,) #define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__) #define DETAIL_TYPEOF_HEAD(x, ...) REM x #define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__), // Strip off the type #define STRIP(x) EAT x // Show the type without parenthesis #define PAIR(x) REM x 

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

 REFLECTABLE ( (const char *) name, (int) age ) 

Поэтому, используя Boost.PP, мы перебираем каждый аргумент и генерируем такие данные:

 // A helper metafunction for adding const to a type template struct make_const { typedef T type; }; template struct make_const { typedef typename boost::add_const::type type; }; #define REFLECTABLE(...) \ static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \ friend struct reflector; \ template \ struct field_data {}; \ BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) #define REFLECT_EACH(r, data, i, x) \ PAIR(x); \ template \ struct field_data \ { \ Self & self; \ field_data(Self & self) : self(self) {} \ \ typename make_const::type & get() \ { \ return self.STRIP(x); \ }\ typename boost::add_const::type & get() const \ { \ return self.STRIP(x); \ }\ const char * name() const \ {\ return BOOST_PP_STRINGIZE(STRIP(x)); \ } \ }; \ 

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

 struct reflector { //Get field_data at index N template static typename T::template field_data get_field_data(T& x) { return typename T::template field_data(x); } // Get the number of fields template struct fields { static const int n = T::fields_n; }; }; 

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

 struct field_visitor { template void operator()(C& c, Visitor v, T) { v(reflector::get_field_data(c)); } }; template void visit_each(C & c, Visitor v) { typedef boost::mpl::range_c::n> range; boost::mpl::for_each(boost::bind(field_visitor(), boost::ref(c), v, _1)); } 

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

 struct Person { Person(const char *name, int age) : name(name), age(age) { } private: REFLECTABLE ( (const char *) name, (int) age ) }; 

Вот обобщенная функция print_fields :

 struct print_visitor { template void operator()(FieldData f) { std::cout << f.name() << "=" << f.get() << std::endl; } }; template void print_fields(T & x) { visit_each(x, print_visitor()); } 

Пример:

 int main() { Person p("Tom", 82); print_fields(p); return 0; } 

Какие результаты:

 name=Tom age=82 

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

Я решил ту же проблему с моей общей структурой, что и код JSON.

Определите макрос: REFLECT (CLASS_NAME, MEMBER_SEQUENCE), где MEMBER_SEQUENCE (имя) (возраст) (другое) (…)

Установите REFLECT в нечто подобное:

 template<> struct reflector { template void visit( Visitor&& v ) { v( "name" , &CLASS_NAME::name ); v( "age", &CLASS_NAME::age ); ... } } 

Вы можете использовать BOOST_PP_SEQ_FOREACH для расширения SEQ в посетителях.

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

 template struct print_visitor { print_visitor( T& s ):self(s){} template void operator( const char* name, R (T::*member) )const { std::cout< void print( const T& val ) { reflector::visit( print_visitor(val) ); } 

http://bytemaster.github.com/mace/group_ mace _reflect__typeinfo.html

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

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

Это прекрасный пример для последовательностей Fusion Fusion ; они могут использоваться для представления времени компиляции. Кроме того, вы можете генерировать более общее поведение во время выполнения.

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

Если ваш тип не соответствует последовательности Fusion (или вы не хотите вмешиваться в его внутренности), в адаптированном разделе есть адаптеры, такие как BOOST_FUSION_ADAPT_STRUCT . И, конечно, поскольку не все – это struct (или имеет публичные элементы), также существует более общая версия для classов, она скоро становится некрасивой: BOOST_FUSION_ADAPT_ADT .

Кража с самого начала :

 struct print_xml { template  void operator()(T const& x) const { std::cout << '<' << typeid(x).name() << '>' << x << "' ; } }; int main() { vector stuff(1, 'x', "howdy"); int i = at_c<0>(stuff); char ch = at_c<1>(stuff); std::string s = at_c<2>(stuff); for_each(stuff, print_xml()); } 

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

 struct Foo { int bar; char const* buzz; }; BOOST_FUSION_ADAPT_STRUCT( Foo, (int, bar) (char const*, buzz) ) 

А потом:

 int main() { Foo foo{1, "Hello"); for_each(foo, print_xml()); } 

Это довольно впечатляющая библиотека 🙂

Вам нужен кортеж, а не class. Это легко разрешит все ваши проблемы, не прибегая к хакерству препроцессоров.

Зачем вам нужен препроцессор? Введение в библиотеку boost.fusion имеет пример, похожий на ваш прецедент.

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

 // http://stackoverflow.com/a/2831966/2725810 #define REFLECTABLE_0(...) \ static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \ friend struct reflector; \ template  struct field_data {}; \ BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, \ BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) #define REFLECTABLE_1(...) \ static const int fields_n = 0; #define REFLECTABLE_CONST2(b, ...) REFLECTABLE_##b(__VA_ARGS__) #define REFLECTABLE_CONST(b, ...) REFLECTABLE_CONST2(b,__VA_ARGS__) #define REFLECTABLE(...) \ REFLECTABLE_CONST(BOOST_PP_IS_EMPTY(__VA_ARGS__), __VA_ARGS__) 
  • Можете ли вы рекомендовать механизм шаблонов .net?
  • JQuery's $ находится в конфликте с StringTemplate.Net в ASP.Net MVC
  • std :: enable_if условно скомпилировать функцию-член
  • Как ссылаться на ресурс CSS / JS / image в шаблоне Facelets?
  • Параметры шаблона шаблона
  • C ++ шаблоны для производительности?
  • Что такое руководства по вычитанию шаблонов и когда мы должны их использовать?
  • std :: function vs template
  • Можете ли вы создавать пользовательские операторы на C ++?
  • Определить, является ли тип указателем в функции шаблона
  • Может ли область переменной Jinja расширяться во внутреннем блоке?
  • Давайте будем гением компьютера.