是否有可能在C 11中写出(S,f)或hasfield(S,f)的宏存在?

前端之家收集整理的这篇文章主要介绍了是否有可能在C 11中写出(S,f)或hasfield(S,f)的宏存在?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我对标准的元编程解决方案(例如 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).

原文链接:https://www.f2er.com/c/118969.html

猜你在找的C&C++相关文章