Представьте, что у нас есть некий протокол с сотнями типов сообщений, каждый из которых мы хотим смоделировать с помощью класса 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
и т. д., и я могу выполнить рекурсию по полям и заставить каждое из них распечатать собственное значение с префиксом своего имени и т. д.
Теперь вопрос: Как я могу написать такой код эффективно, без повторений?
В идеале я хочу иметь возможность сказать следующее:
START_MESSAGE(Message)
ADDFIELD(int, header)
ADDFIELD(double, temperature)
ADDFIELD(char, flag)
END_MESSAGE
Есть ли какой-нибудь способ, комбинируя препроцессор, Boost и C++11, добиться чего-то подобного без необходимости использования внешних инструментов генерации? (Я думаю, что Boost.Preprocessor называет это «горизонтальным» и «вертикальным» повторением. Мне нужно как-то «транспонировать» данные поля.) Ключевой особенностью здесь является то, что мне никогда не приходится повторять какую-либо информацию, а изменение или добавление одного поля требует только одного единственного изменения.