Imagine que tenemos algún tipo de protocolo con cientos de tipos de mensajes, cada uno de los cuales queremos modelar por una clase C++. Debido a que cada clase debe ser capaz de procesar cada campo de forma automática, una solución natural es simplemente tener una std::tuple
con todos los tipos requeridos:¿Cómo diseñar una clase con campos "anotados"?
std::tuple<int, double, char> message;
print(message); // the usual variadic magic
todo esto está bien y bien. Sin embargo, ahora quiero dar un nombre a cada campo, y quiero poder usar el nombre cuando me refiera al campo en mi código, así como obtener una representación textual de él. Ingenuamente, o en C, podría haber escrito:
struct Message
{
int header;
double temperature;
char flag;
};
De esa manera se pierde la capacidad de procesamiento automagic recursiva de la tupla, pero podemos nombrar cada campo literalmente. En C++, podemos hacer ambas cosas por medio de una enumeración:
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;
};
Ahora puedo decir, Message m; m.get<Message::header>() = 12;
etc., y puedo recursivo sobre los campos y hacer que cada imprimir su propio valor prefijado por su propio nombre, etc.
Ahora la pregunta: ¿Cómo puedo autor dicho código de manera eficiente, sin repetición?
Idealmente, quiero ser capaz de decir esto:
START_MESSAGE(Message)
ADDFIELD(int, header)
ADDFIELD(double, temperature)
ADDFIELD(char, flag)
END_MESSAGE
¿Hay alguna manera, la combinación de preprocesador, Boost y C++ 11, para lograr algo como esto sin la necesidad de herramientas de generación externos ? (Creo que Boost.Preprocessor llama a esta repetición "horizontal" y "vertical". Necesito "transponer" los datos de campo de alguna manera). La característica clave aquí es que nunca tengo que repetir ninguna información, y que modificar o agregar un campo solo requiere un cambio único.
Para este tipo de problemas, un lenguaje descriptivo simple y un preprocesador encargo generar un archivo de inclusión es mejor a largo plazo. El "archivo fuente" es fácil de mantener e incluso puede ser generado por herramientas externas si el proyecto lo requiere. –
@AlexandreC .: En cualquier etapa, "una herramienta más pequeña" siempre se ve como la mejor respuesta. Pero en el gran esquema de cosas, solo tienes una cosa más que llevar contigo y mantener, documentar, recordar y entrenar a la gente. Tener algo que funcione de la caja definitivamente vale la pena el dolor de configurar algunas macros horrendas. –