C ++预处理器:避免代码重复成员variables列表

我有一个多个类,每个都有不同的成员variables,在构造函数中被初始化。 这里是一个例子:

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

每个都有一个关联的print<>()函数。

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

或者更好,基于模板的方法?

请不要质疑我为什么要这样做,有理由的devise决定导致多个具有命名参数的类似对象。 出于性能原因,参数需要命名为成员variables。 我只是在探索是否可以只列出一次参数和types。

你需要做的是预处理器生成有关字段的reflection数据。 这些数据可以存储为嵌套类。

首先,为了使它在预处理器中更容易和更清晰,我们将使用types化expression式。 一个types化的expression式只是一个将该types放在括号中的expression式。 所以,而不是写int x你会写(int) x 。 这里有一些方便的macros来帮助键入expression式:

 #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 

接下来,我们定义一个REFLECTABLEmacros来生成关于每个字段的数据(加上字段本身)。 这个macros将被这样调用:

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

所以使用Boost.PP我们迭代每个参数并生成如下的数据:

 // 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)); \ } \ }; \ 

它所做的是生成一个常量fields_n ,它是类中可reflection字段的数量。 然后专门为每个领域的field_data 。 它也是reflector类的朋友,所以即使它们是私有的也可以访问这些字段:

 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; }; }; 

现在遍历我们使用访问者模式的字段。 我们创build一个从0到字段数的MPL范围,并访问该索引处的字段数据。 然后它将现场数据传递给用户提供的访问者:

 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)); } 

现在,我们把这一切放在一起。 以下是我们如何定义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<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()); } 

一个例子:

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

哪些产出:

 name=Tom age=82 

瞧,我们刚刚在C ++中用100行代码实现了reflection。

我已经解决了与我的generics结构到JSON代码相同的问题。

定义一个macros:REFLECT(CLASS_NAME,MEMBER_SEQUENCE)其中MEMBER_SEQUENCE是(name)(age)(other)(…)

反思扩大到类似于:

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

您可以使用BOOST_PP_SEQ_FOREACH将SEQ扩展到访问者。

然后定义你的打印访问者:

 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

恐怕你的解决scheme对于这个简化的用例来说是非常理想的。 我们可以提供帮助的是如果除了print之外还有其他function,可以通过遍历字段获益。

这是Boost.Fusion 融合序列的完美例子; 他们可以用来引入编译时reflection。 最重要的是,您可以生成更多通用的运行时行为。

所以,你可以例如使用Fusion.Map来声明你的元素(这会限制你每种types的一次出现)或者其他这样的幻想。

如果你的types不符合一个融合序列(或者你不想干涉它的内部),在适配的部分有适配器,例如BOOST_FUSION_ADAPT_STRUCT 。 当然,因为不是所有的东西都是struct (或者是公共成员),所以类也有一个更通用的版本,它很快会变得难看: BOOST_FUSION_ADAPT_ADT

从快速入门偷窃:

 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()); } 

适配器会让你“适应”一种types,所以你会得到:

 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级。 这将很容易解决你所有的问题,而不必诉诸于预处理器hackery。

为什么你需要使用预处理器? boost.fusion库的介绍有一个例子有点类似于你的用例。

这是我的2美分作为除了保罗伟大的REFLECTABLEmacros。 我需要有一个空的列表,即REFLECTABLE()来正确处理inheritance层次结构。 以下修改处理这种情况:

 // 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__)