C ++ preprocessor: avoid repeating code of a list of member variables

I have several classes with different member variables that are trivially initialized in the constructor. Here is an example:

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

Each has an associated print<>() function.

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

This code is error prone because the parameter list is replicated in four places. How can I rewrite code to avoid this duplication? I would like to use a preprocessor and / or templates.

For example, is it possible to use the X-args preprocessor technique - something like this?

 #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<Person>(const Person &person) { LIST_COUT_LINE(ARGUMENTS) } #undef ARGUMENTS 

Or better, a boilerplate approach?

Please do not ask why I want to do this; there are sound design decisions that led to the creation of several similar objects with named parameters. For performance reasons, parameters should be called member variables. I just study whether it is possible to list parameters and their types only once.

+50
c ++ c-preprocessor arguments templates
Jun 14 2018-12-12T00:
source share
6 answers

What you need to do is that the preprocessor generates the reflection data in the fields. This data can be stored as nested classes.

First, to simplify and clean it in the preprocessor, we will use a typed expression. A typed expression is simply an expression that puts the type in parentheses. So instead of writing int x you will write (int) x . Here are some useful macros to help with typed expressions:

 #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 

Next, we define the REFLECTABLE macro to generate data about each field (plus the field itself). This macro will be called like this:

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

Thus, using Boost.PP , we iterate over each argument and generate such data:

 // A helper metafunction for adding const to a type template<class M, class T> struct make_const { typedef T type; }; template<class M, class T> struct make_const<const M, T> { typedef typename boost::add_const<T>::type type; }; #define REFLECTABLE(...) \ static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \ friend struct reflector; \ template<int N, class Self> \ 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<class Self> \ struct field_data<i, Self> \ { \ Self & self; \ field_data(Self & self) : self(self) {} \ \ typename make_const<Self, TYPEOF(x)>::type & get() \ { \ return self.STRIP(x); \ }\ typename boost::add_const<TYPEOF(x)>::type & get() const \ { \ return self.STRIP(x); \ }\ const char * name() const \ {\ return BOOST_PP_STRINGIZE(STRIP(x)); \ } \ }; \ 

What it is, generates the fields_n constant, which is the number of reflected fields in the class. He then specializes in field_data for each field. It also supports the reflector class, so it can access fields even if they are private:

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

Now, to iterate over the fields, we use the visitor template. We create an MPL range from 0 to the number of fields and get access to the field data at this index. Then it passes the field data to the visitor to whom the user is provided:

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

Now, for a moment of truth, we are all together. Here is how we can define the Person class:

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

Here is the generic print_fields function:

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

Example:

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

What outputs:

 name=Tom age=82 

And voila, we just implemented reflection in C ++, within up to 100 lines of code.

+184
Jul 31 '12 at 16:29
source share

I solved the same problem with my general structure as the JSON code.

Define a macro: REFLECT (CLASS_NAME, MEMBER_SEQUENCE) where MEMBER_SEQUENCE is (name) (age) (other) (...)

For REFLECT, deploy something similar to:

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

You can use BOOST_PP_SEQ_FOREACH to deploy SEQ to visitors.

Then identify the print visitor:

 template<typename T> struct print_visitor { print_visitor( T& s ):self(s){} template<typename R> void operator( const char* name, R (T::*member) )const { std::cout<<name<<"= "<<self.*member<<std::endl; } T& self; } template<typename T> void print( const T& val ) { reflector<T>::visit( print_visitor<T>(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

+5
Jul 29 '12 at 17:51
source share

I am afraid that your solution is quite optimal for this shorthand utility. We can help if you have additional functions besides print , which will benefit from field iteration.

This is a great example for Boost.Fusion Fusion Sequences ; they can be used to represent compilation time. In addition, you can generate more general runtime behavior.

So, you can, for example, declare your elements using Fusion.Map (which limits you to one occurrence of each type) or other such fantasies.

If your type does not match the Fusion sequence (or you do not want to interfere with its internals), adapted , for example, BOOST_FUSION_ADAPT_STRUCT . And, of course, since not everything is struct (or has public elements), there is also a more general version for classes, it will soon become ugly: BOOST_FUSION_ADAPT_ADT .

Quick Start Overview:

 struct print_xml { template <typename T> void operator()(T const& x) const { std::cout << '<' << typeid(x).name() << '>' << x << "</" << typeid(x).name() << '>' ; } }; int main() { vector<int, char, std::string> 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()); } 

Adapters allow you to "adapt" the type, so you get:

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

And then:

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

This is a pretty impressive library :)

+4
Aug 01 '12 at 18:22
source share

You need a tuple, not a class. This will easily solve all your problems without resorting to hacking preprocessors.

+1
Jul 31 2018-12-12T00:
source share

Why do you need a preprocessor? An introduction to the boost.fusion library has an example similar to your use case.

+1
Aug 01 '12 at 6:19 06:19
source share

Here are my 2 cents as an addition to Paul’s great REFLECTABLE macro. I needed to have an empty list of fields, i.e. REFLECTABLE() , for the proper handling of the inheritance hierarchy. The following modification handles this case:

 // http://stackoverflow.com/a/2831966/2725810 #define REFLECTABLE_0(...) \ static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \ friend struct reflector; \ template <int N, class Self> 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__) 
0
Dec 11 '15 at 9:50
source share



All Articles