我对标准的元编程解决方案(例如
C++11 ways of finding if a type has member function or supports operator?)非常熟悉,它不涉及宏观魔法.但是,我有一个涉及以下便利宏的用例(当然,对于StackOverflow大大简化了,但想象这是用于序列化的东西)……
#define START(type) do { typedef type current; const char typeName[] = #type #define OUTPUT(fieldname) \ printf("type of %s.%s is %s\n",#type,#fieldname,\ std::is_same<decltype(std::declval<current>().fieldname),int> ? "int" : "string") #define END() } while (0) struct Foo { int i; char *j; char *k; }; struct Bar { char *x; int y; }; START(Foo); OUTPUT(i); // type of Foo.i is int OUTPUT(j); // type of Foo.j is string OUTPUT(k); // type of Foo.k is string END(); START(Bar); OUTPUT(x); // type of Bar.x is string OUTPUT(y); // type of Bar.y is int END();
但现在让我们说有人出现并为我们的架构添加了一种新的数据成员:字段对(x,xLength).我们想要像这样改变我们的便利宏……
#define START(obj) do { const auto& current = (obj) #define OUTPUT(fieldname) \ printf("type of %s.%s is %s\n",int> ? "int" : hasfield(current,fieldname##Length) ? "Pascal string" : "C string") #define END() } while (0) struct Baz { char *x,*y,*z; int xLength,zLength; }; START(Baz); OUTPUT(x); // type of Baz.x is Pascal string OUTPUT(y); // type of Baz.y is C string OUTPUT(z); // type of Baz.z is Pascal string END();
就我自己而言,我设法提出了以下适用于Clang的hasfield实现…
#define hasfield(classtype,fieldname) \ []() { \ struct X { \ template<class T,int=sizeof(&T::fieldname)> static constexpr bool f(T*){ return true; } \ static constexpr bool f(...) { return false; } \ }; return X::f((classtype*)0); \ }()
……但不幸的是,这似乎是由于a bug in Clang;根据C 11标准,本地类X不允许具有模板成员.实际上,这段代码无法与GCC一起编译.
所以我很难过:在C 11中是否有可能定义OUTPUT宏以便它能做我想做的事情?
绝对约束:不改变Baz的结构定义.没有提前对字段名进行硬编码.
Nice-to-haves:一个hasfield(c,f)宏,也可以在其他上下文中使用(而不是将代码直接缠绕到OUTPUT宏中).假设offsetof(c,fLength)== offsetof(c,f)sizeof(std :: declval< c>().f).
解决方法
通过继承当前和依赖阴影,可以使用一些可能或可能不重要的限制:声明一个本地fieldname变量,创建一个从你正在检查的类型派生的本地类,并在成员函数,检查fieldname是否仍然引用局部变量.如果是,则不存在成员字段名.
#include <utility> #include <stdio.h> #define START(type) do { typedef type current; const char typeName[] = #type #define HASMEMBER(fieldname) \ []() -> bool { \ struct HASMEMBER1 { } fieldname; \ struct HASMEMBER2 : current { \ static char TEST1(HASMEMBER1&); \ static char (&TEST1(...))[2]; \ auto TEST2() -> decltype(TEST1(fieldname)); \ }; \ return sizeof(std::declval<HASMEMBER2>().TEST2()) == 2; \ }() #define OUTPUT(fieldname) \ printf("type of %s.%s is %s\n",typeName,\ std::is_same<decltype(current::fieldname),int>::value ? "int" : \ HASMEMBER(fieldname##Length) ? "Pascal string" : "C string") #define END() } while (0) struct Foo { int i; char *j; char *k; }; struct Bar { char *x; int y; }; struct Baz { char *x,zLength; }; int main() { START(Foo); OUTPUT(i); // type of Foo.i is int OUTPUT(j); // type of Foo.j is C string OUTPUT(k); // type of Foo.k is C string END(); START(Bar); OUTPUT(x); // type of Bar.x is C string OUTPUT(y); // type of Bar.y is int END(); START(Baz); OUTPUT(x); // type of Baz.x is Pascal string OUTPUT(y); // type of Baz.y is C string OUTPUT(z); // type of Baz.z is Pascal string END(); }
编辑参加GCC 4.6.3.它仍然被GCC 4.8.1和clang 3.3接受,并且也应该与GCC 4.7.3一起使用(但不是4.7.2).