Как разработать класс с «аннотированными» полями?

Представьте, что у нас есть некий протокол с сотнями типов сообщений, каждый из которых мы хотим смоделировать с помощью класса 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() = 12;и т. д., и я могу выполнить рекурсию по полям и заставить каждое из них распечатать собственное значение с префиксом своего имени и т. д.


Теперь вопрос: Как я могу написать такой код эффективно, без повторений?

В идеале я хочу иметь возможность сказать следующее:

START_MESSAGE(Message)
ADDFIELD(int, header)
ADDFIELD(double, temperature)
ADDFIELD(char, flag)
END_MESSAGE

Есть ли какой-нибудь способ, комбинируя препроцессор, Boost и C++11, добиться чего-то подобного без необходимости использования внешних инструментов генерации? (Я думаю, что Boost.Preprocessor называет это «горизонтальным» и «вертикальным» повторением. Мне нужно как-то «транспонировать» данные поля.) Ключевой особенностью здесь является то, что мне никогда не приходится повторять какую-либо информацию, а изменение или добавление одного поля требует только одного единственного изменения.

9
задан Kerrek SB 27 March 2012 в 12:22
поделиться