Imagine we have some sort of protocol with hundreds of message types, each of which we want to model by a C++ class. Since each class should be able to process each field automatically, a natural solution is to just have an std::tuple
with all the required types:
std::tuple<int, double, char> message;
print(message); // the usual variadic magic
This is all fine and well. However, now I want to give each field a name, and I want to be able to use the name when referring to the field in my code, as well as get a textual representation of it. Naively, or in C, I might have written:
struct Message
{
int header;
double temperature;
char flag;
};
That way we lose the recursive automagic processing power of the tuple, but we can name each field literally. In C++, we can do both by means of an enum:
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;
};
Now I can say, Message m; m.get<Message::header>() = 12;
etc., and I can recurse over the fields and make each print out their own value prefixed by their own name, etc.
Now the question: How can I author such code efficiently, without repetition?
Ideally, I want to be able to say this:
START_MESSAGE(Message)
ADDFIELD(int, header)
ADDFIELD(double, temperature)
ADDFIELD(char, flag)
END_MESSAGE
Is there any way, combining preprocessor, Boost and C++11, to achieve something like this without the need for external generation tools? (I think Boost.Preprocessor calls this "horizontal" and "vertical" repetition. I need to "transpose" the field data somehow.) The key feature here is that I never have to repeat any of the information, and that modifying or adding one field only requires one single change.