5

Estoy desarrollando una aplicación C++ utilizada para simular un escenario del mundo real. En base a esta simulación, nuestro equipo desarrollará, probará y evaluará diferentes algoritmos que funcionan dentro de un escenario de mundo real.Configuración de aplicación flexible en C++

Necesitamos la posibilidad de definir varios escenarios (pueden diferir en unos pocos parámetros, pero un escenario futuro también podría requerir la creación de objetos de nuevas clases) y la posibilidad de mantener un conjunto de algoritmos (que es, de nuevo, un conjunto de parámetros, sino también la definición de las clases que se crearán). Los parámetros se pasan a las clases en el constructor.

Me pregunto cuál es la mejor forma de administrar todas las configuraciones de escenarios y algoritmos. Debería ser posible que un desarrollador trabaje en un escenario con "su" algoritmo y otro desarrollador trabajando en otro escenario con "su" algoritmo diferente. Aún así, los conjuntos de parámetros pueden ser enormes y deben ser "compartibles" (si definí un conjunto de parámetros para un determinado algoritmo en el Escenario A, debería ser posible usar el algoritmo en el Escenario B sin copiar & pegar).

Parece que hay dos formas principales para llevar a cabo mi tarea:

  • definir un formato de archivo de configuración que puede manejar mis necesidades. Este formato puede estar basado en XML o personalizado. Como no hay reflejo de C# en C++, parece que tengo que actualizar el analizador de archivos de configuración cada vez que se agrega una nueva clase de algoritmo al proyecto (para convertir una cadena como "MyClass" en una nueva instancia de MyClass) Podría crear un nombre para cada configuración y pasar este nombre como argumento de línea de comando.

    • Las ventajas son: no compilación necesario para cambiar un parámetro y volver a ejecutar, que puede almacenar fácilmente todo el archivo de configuración con la simulación resultados
    • Contra: parece como un montón de esfuerzo, especialmente duro porque estoy usando muchas clases de plantillas que tienen que crearse una instancia con argumentos de plantilla dados. No hay soporte IDE para escribir el archivo (por lo menos sin crear todo un XSD, que tendría que actualizar cada vez que se añade un parámetro/clase)
  • alambre todo para arriba en código C++. No estoy completamente seguro de cómo haría esto para separar toda la lógica de creación diferente, pero aún así poder reutilizar los parámetros en todos los escenarios. Creo que también trataría de darle a cada configuración un nombre (cadena) y usar este nombre para seleccionar la configuración a través de la línea de comando arg.

    • Pro: la seguridad de tipos, soporte IDE, sin analizador necesario
    • con: (¿tal vez algo de serialización) ¿cómo puedo almacenar fácilmente la configuración con los resultados ?, necesita compilación después de cada cambio de parámetro

Ahora aquí están mis preguntas: - ¿Cuál es su opinión? ¿Extrañé pros/contras importantes? - ¿Perdí una tercera opción? - ¿Existe una manera simple de implementar el enfoque de archivo de configuración que le da a suficiente flexibilidad? - ¿Cómo organizarías todo el código de fábrica en el segundo enfoque? ¿Hay algún buen ejemplo de C++ para algo como esto?

¡Muchas gracias!

Respuesta

-1

Encontré this website con una bonita plantilla de apoyo de fábrica que creo que se utilizará en mi código.

0

Si no utiliza XML, es posible que boost :: spirit podría provocar un cortocircuito en al menos algunos de los problemas que enfrenta. Aquí hay un simple example de cómo los datos de configuración se pueden analizar directamente en una instancia de clase.

+0

Gracias, Steve. No he visto esto antes y parece bastante bueno tener parámetros en los archivos de configuración, pero todavía parece mucho trabajo conseguir mis argumentos de plantilla en ... – Philipp

2

Hay una manera de hacerlo sin plantillas ni reflejo.

Primero, asegúrese de que todas las clases que desea crear desde el archivo de configuración tienen una clase base común. Llamemos a esto MyBaseClass y supongamos que MyClass1, MyClass2 y MyClass3 todos heredan de él. En segundo lugar, implementa una función de fábrica para cada uno de MyClass1, MyClass2 y MyClass3. Las firmas de todas estas funciones de fábrica deben ser idénticas. Una función de fábrica de ejemplo es la siguiente.

MyBaseClass * create_MyClass1(Configuration & cfg) 
{ 
    // Retrieve config variables and pass as parameters 
    // to the constructor 
    int age = cfg->lookupInt("age"); 
    std::string address = cfg->lookupString("address"); 
    return new MyClass1(age, address); 
} 

En tercer lugar, que registra todas las funciones de la fábrica en un mapa.

typedef MyBaseClass* (*FactoryFunc)(Configuration *); std::map<std::string, FactoryFunc> nameToFactoryFunc; nameToFactoryFunc["MyClass1"] = &create_MyClass1; nameToFactoryFunc["MyClass2"] = &create_MyClass2; nameToFactoryFunc["MyClass3"] = &create_MyClass3;

Por último, analizar el archivo de configuración y iterar sobre ella para encontrar todas las entradas que especifican el nombre de una clase. Cuando encuentre tal entrada, busque su función de fábrica en la tabla nameToFactoryFunc e invoque la función para crear el objeto correspondiente.

+0

Al implementar las funciones 'create_ *' con la misma firma y diferente tipo de devolución, básicamente hiciste el trabajo de las plantillas a mano. No digo que sea malo, pero creo que sería más claro si utilizaras una función 'create' con plantillas y la especializaras para cada subtipo. –

+0

La decisión sobre qué función de plantilla llamar se realiza en tiempo de compilación. Por el contrario, el uso de un mapa _name-> factory function_ permite que la decisión se tome en tiempo de ejecución, por lo que es una forma de polimorfismo. –

+0

En el momento de la compilación, solo ocurre la deducción del tipo: todavía puede mantener punteros a las funciones de plantilla: https://stackoverflow.com/questions/3641657/address-of-c-template-function –

Cuestiones relacionadas