我相当熟悉不涉及宏魔术的标准元编程解决方案(例如 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", #type, #fieldname, \
std::is_same<decltype(std::declval<current>().fieldname),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();
我自己设法想出了以下 hasfield
的实现适用于 Clang...
#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
的结构定义.没有硬编码 fieldname
提前。
可有可无:A hasfield(c,f)
也可以在其他上下文中使用的宏(而不是将代码直接缠绕到 OUTPUT
宏中)。不假设 offsetof(c,fLength)==offsetof(c,f)+sizeof(std::declval<c>().f)
.
最佳答案
通过从 current
继承并依赖于阴影,可以使它在某些限制下工作,这些限制对您来说可能重要也可能不重要:声明一个本地 fieldname
变量,创建一个派生自您正在检查的类型的局部类,并在成员函数内部检查 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, #fieldname, \
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, *y, *z; int xLength, 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)。
关于c++ - 是否可以在 C++11 中编写宏 existenceof(S,f) 或 hasfield(S,f)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18925788/