是否可以在 C++11 中编写宏存在 (S,f) 或 hasfield(S,f)
Is it possible to write a macro existenceof(S,f) or hasfield(S,f) in C++11?
我相当熟悉不涉及宏魔法的标准元编程解决方案(例如C++查找类型是否具有成员函数或支持运算符的 11 种方法?但是,我有一个用例涉及以下便利宏(当然,针对 StackOverflow 进行了大幅简化,但想象一下这是用于序列化或其他东西)......
#define START(type) do { typedef type current; const char typeName[] = #type
#define OUTPUT(fieldname)
printf("type of %s.%s is %sn", #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 %sn", #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();
我自己设法想出了以下适用于 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);
}()
。但不幸的是,这似乎是由于 Clang 中的一个错误;根据 C++11 标准,本地类X
不允许具有模板成员。实际上,此代码无法使用 GCC 进行编译。
所以我很困惑:是否可以在 C++11 中定义OUTPUT
宏,使其能够执行我想要的操作?
绝对约束:不更改Baz
的结构定义。无需提前对fieldname
进行硬编码。
Nice-to-haves:一个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 %sn", 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)。
感谢@hvd寻找局部变量或继承成员的聪明想法!以下是我最终在GCC 4.6.3上获得的确切代码:
#include <utility>
#include <stdio.h>
#define START(type) do { typedef type current; const char typeName[] = #type
#define HASMEMBER(fieldname)
[]()->bool {
char fieldname;
struct HASMEMBER2 : current {
auto TEST2() -> char[sizeof(fieldname)];
};
return sizeof(std::declval<HASMEMBER2>().TEST2()) != 1;
}()
#define OUTPUT(fieldname)
printf("type of %s.%s is %sn", 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();
}
请注意,此方法无法区分 1 字节长的char xLength
和缺少xLength
。对于我的申请,这是可以接受的;我所有的xLength
字段要么是 4 个字节长,要么不存在。
请注意,此HASMEMBER
仅适用于非私有成员变量;它不应用于测试私有成员 (duh) 或成员函数。同样,这对于我的应用程序来说是可以接受的。
作为记录,GCC 4.6.3 有一些错误使其拒绝编译 auto member_func() -> decltype(data_member)
,但它对auto member_func() -> char[sizeof data_member]
感到满意。
- C++模板来检查友元函数的存在
- 既然存在危险,为什么项目要使用-I include开关
- 我们可以访问一个不存在的联盟的成员吗
- C++:对不存在的命名空间使用命名空间指令
- C++quit()函数中可能存在作用域问题
- C++擦除(如果存在)
- g++ 说函数不存在,即使包含正确的标头
- 这个极客对极客的trie实现是否存在内存泄漏问题
- 有了gcc,是否可以链接库,但前提是它存在
- C++LinkedList问题.数据类型之间存在冲突?没有匹配的构造函数
- gcc和clang在表达式是否为常量求值的问题上存在分歧
- C++Builder中的OnClick事件签名存在问题
- 如何正确地将分支添加到已存在的树中
- 我知道函数调用中存在歧义.有没有办法调用foo()函数
- 如何检查QList中是否存在值
- 根据某个函数是否存在启用模板
- 如何将分支添加到已存在的TTree:ROOT
- 地图计数确实很重要,或者只是检查是否存在
- 通用C++/Python 多语言的存在
- 是否可以在 C++11 中编写宏存在 (S,f) 或 hasfield(S,f)