【发布时间】:2012-03-27 20:20:44
【问题描述】:
假设我们有某种协议,其中包含数百种消息类型,我们希望通过 C++ 类对每种类型进行建模。由于每个类都应该能够自动处理每个字段,一个自然的解决方案是只拥有一个包含所有必需类型的 std::tuple:
std::tuple<int, double, char> message;
print(message); // the usual variadic magic
这一切都很好。但是,现在我想给每个字段一个名称,并且我希望能够在我的代码中引用该字段时使用该名称,并获得它的文本表示。天真地,或者在 C 中,我可能会写:
struct Message
{
int header;
double temperature;
char flag;
};
这样我们就失去了元组的递归自动处理能力,但我们可以按字面意思命名每个字段。在 C++ 中,我们可以通过枚举来做到这两点:
struct Message
{
enum FieldID { header, temperature, flag };
static const char * FieldNames[] = { "header", "temperature", "flag" };
typedef std::tuple<int, double, char> tuple_type;
template <FieldID I>
typename std::tuple_element<I, tuple_type>::type & get()
{ return std::get<I>(data); }
template <FieldID I>
static const char * name() { return FieldNames[I]; }
tuple_type data;
};
现在我可以说,Message m; m.get<Message::header>() = 12; 等,我可以对字段进行递归,并让每个字段打印出以自己的名称为前缀的自己的值,等等。
现在的问题是:我怎样才能有效地编写这样的代码而不重复?
理想情况下,我希望能够这样说:
START_MESSAGE(Message)
ADDFIELD(int, header)
ADDFIELD(double, temperature)
ADDFIELD(char, flag)
END_MESSAGE
有没有什么方法可以结合预处理器、Boost 和 C++11 来实现这样的目标,而不需要外部生成工具? (我认为 Boost.Preprocessor 称之为“水平”和“垂直”重复。我需要以某种方式“转置”字段数据。)这里的关键特征是我永远不必重复任何信息,并且修改或添加一个字段只需要一次更改。
【问题讨论】:
-
对于此类问题,从长远来看,最好使用简单的描述性语言和生成包含文件的自定义预处理器。 “源文件”易于维护,甚至可以在项目需要时由外部工具生成。
-
@AlexandreC.:在任何给定阶段,“再多一个小工具”总是看起来更好的答案。但在宏伟的计划中,你只需要携带一件东西来维护、记录、记住和培训人们。开箱即用的东西绝对值得设置一些可怕的宏的痛苦。
标签: c++ class-design field