【发布时间】:2013-05-03 03:25:59
【问题描述】:
假设我要定义以下结构的类:
struct MyClass {
int x;
bool y;
float z;
MyClass(QVariantMap data) : x(data["x"]), y(data["y"]), z(data["z"]) {}
};
如您所见,我有一个 QVariantMap(类似于std::map<std::string, boost::variant<...>>,对于那些不熟悉 Qt 的人),我希望能够在不进一步了解其字段的情况下构造这样的类型,因此方便的构造函数,它应该“反序列化”地图中的字段。
我需要几个这种风格的类,并且我希望定义尽可能简洁,以实现最大的可维护性(关于字符串键中的拼写错误)、可读性和自动化。
我想到了如下的宏观结构:
DEF_CLASS(MyClass)(
DEF_FIELD(int, x)
DEF_FIELD(bool, y)
DEF_FIELD(float, z)
);
当我只想生成字段而不是构造函数时,我没有看到任何问题(反过来也可以,但我只演示前者):
#define DEF_CLASS(CLASSNAME) \
struct CLASSNAME { \
_DEF_CLASS_TAIL/*..."curry the arguments"...*/
#define _DEF_CLASS_TAIL(FIELDS) \
FIELDS \
}
#define DEF_FIELD(TYPE, NAME) TYPE NAME;
我定义了启动类定义的第一个宏,并使用“curry”技术将第二个括号转发到第二个宏,该宏放置类定义的内容 (FIELDS),然后将其关闭。这样——我的想法也是如此——我可以在第二个宏中访问FIELDS。
但是我现在怎样才能输出两次字段,一次定义实际字段,一次输出成员初始化?
我知道,如果我以两种不同的方式定义宏 DEF_CLASS_FIELD,然后在上面的定义代码中稍微“包含”字段,一个用于每个宏定义的字段,我可以一个接一个地正确打印它们。但是由于字段列表是(并且应该)在类定义中,所以我不能简单地包含两次。
还有其他选择吗?
我尽量避免使用 Boost 预处理器库,但如果您有一个很好的解决方案可以使用它,请继续。不过,如果有的话,我更喜欢简单的解决方案。
【问题讨论】:
-
这不是您想要的解决方案,但最简单的当然是在结构中保留指向 QVariantMap 的引用/指针。由于您的所有结构都会不同(或者您不需要宏),因此您可能必须在某个地方编写专门针对每个特定结构的代码。在该代码中,您可以缓存结构持有的 QVariantMap 中的值,或者每次只使用映射查找。
-
是的,实际上我当前的临时解决方案正是......我将变量映射存储在类中,并让宏为每个“字段”生成一个 getter,并为每个“字段”提供访问(和转换)称呼。我对这种方法的主要问题不是性能,而是地图是否真的包含预期字段的“后期检查”。如果数据格式错误,我想在构造函数中调用一些错误处理程序(或抛出异常)...
-
好吧,如果您每次都可以进行地图查找,那么就不需要缓存这些值。在这种情况下,代替 DEF_FIELD,也许你可以只创建一个宏,它接受一个映射键(你可以使用
#x来生成"x")和一个类型,然后执行检查:if (typeid(map[KEY])!=typeid(TYPE)) { throw; }?跨度>
标签: c++ c-preprocessor metaprogramming code-generation