2012-09-03 12 views
11

Recientemente comencé a usar boost::program_options y me pareció muy conveniente. Dicho esto, falta una cosa: no pude codificarme de la mejor manera:boost :: program_options: iterando e imprimiendo todas las opciones

Me gustaría iterar sobre todas las opciones que se han recopilado en un boost::program_options::variables_map para mostrarlas en la pantalla. Esto debería convertirse en una función de conveniencia, que simplemente puedo llamar para enumerar todas las opciones que se establecieron sin la necesidad de actualizar la función cuando agregue nuevas opciones o para cada programa.

Sé que puedo verificar y dar salida a las opciones individuales, pero como se dijo anteriormente, esto debería convertirse en una solución general que no tenga en cuenta las opciones reales. Además sé que puedo iterar sobre el contenido de variables_map ya que es simplemente un std::map extendido. Podría verificar el tipo de contenido en la variable almacenada boost::any y usar .as<> para convertirlo de nuevo al tipo apropiado. Pero esto significaría codificar un bloque de interruptor largo con un caso para cada tipo. Y esto no se ve como un buen estilo de codificación para mí.

Entonces la pregunta es, ¿hay una mejor manera de iterar sobre estas opciones y darles salida?

Respuesta

5

Es un buen caso para utilizar el patrón de visitante. Lamentablemente boost::any no es compatible con el patrón de visitante como boost::variant. Sin embargo, hay algunos terceros approaches.

Otra posible idea es usar RTTI: crear un mapa de type_info de tipos conocidos asignados al manejador de tipos de manejadores.

+0

Gracias por el enlace y la idea acerca de RTTI . Tenía la esperanza de poder evitar crear una estructura para todos los tipos soportados que tendría que gestionar si los tipos aumentan, pero parece que esto no será posible. Básicamente, quería pasar el dinero a los tipos, como si admitieran 'operator <<' todo funciona bien, de lo contrario la compilación debería fallar. – shiin

5

Como @Rost anteriormente mencionado, el patrón de visitante es una buena opción aquí. Para usarlo con PO, necesita usar notificadores para sus opciones de tal manera que si se pasa la opción, el notificador llenará una entrada en su conjunto de valores boost::variant. El conjunto debe almacenarse por separado. Después de eso, puede iterar sobre su conjunto y procesar automáticamente las acciones (es decir, imprimir) en ellos usando boost::apply_visitor.

Para los visitantes, heredan de boost::static_visitor<>

En realidad, hice uso de Visitantes y enfoque genérico más amplio.

Creé un class MyOption que contiene la descripción, boost::variant para el valor y otras opciones como implícito, predeterminado y así sucesivamente. Lleno un vector de objetos del tipo MyOption de la misma manera que PO do para sus opciones (ver boost::po::options_add()) a través de plantillas. En el momento de pasar std::string() o double() para boosts::varian t inicialización se completa el tipo del valor y otras cosas como por defecto, implícito.

Después de eso utilicé el patrón de visitante para llenar el contenedor boost::po::options_description ya que boost::po necesita sus propias estructuras para analizar la línea de comando de entrada. Durante el llenado, configuré el notificador para cada opción; si se aprueba, boost::po llenará automáticamente mi objeto original de MyOption.

A continuación debe ejecutar po::parse y po::notify. Después de eso, podrá utilizar el std::vector<MyOption*> ya rellenado mediante el patrón Visitor, ya que contiene boost :: variant inside.

Lo bueno de todo esto - usted debe escribir su tipo de opción una sola vez en el código - al llenar su std::vector<MyOption*>.

PS. si utiliza este enfoque, se encontrará con un problema al configurar el notificador para una opción sin valor, consulte este tema para obtener una solución: boost-program-options: notifier for options with no value

PS2. Ejemplo de código:

std::vector<MyOptionDef> options; 
OptionsEasyAdd(options) 
    ("opt1", double(), "description1") 
    ("opt2", std::string(), "description2") 
... 
; 
po::options_descripton boost_descriptions; 
AddDescriptionAndNotifyerForBoostVisitor add_decr_visitor(boost_descriptions); 
// here all notifiers will be set automatically for correct work with each options' value type 
for_each(options.begin(), options.end(), boost::apply_visitor(add_descr_visitor)); 
+0

Gracias por la explicación completa. Esta es también una solución interesante. De esta forma, también podría agregar fácilmente diferentes descripciones para la salida de ayuda y simplemente cuando se enumeren los valores de las opciones. – shiin

0

Hoy me enfrentaba a este tipo de problema. Esta es una vieja pregunta, pero quizás esto ayudará a las personas que están buscando una respuesta.

El método que se me ocurrió es probar un montón de como < ...>() y luego ignorar la excepción. No es terriblemente bonito, pero lo hice funcionar.

En el siguiente bloque de código, vm es un variables_map de boost program_options. vit es un iterador sobre vm, lo que lo convierte en un par de std :: string y boost :: program_options :: variable_value, siendo este último un boost :: any. Puedo imprimir el nombre de la variable con vit-> primero, pero vit-> second no es tan fácil de generar porque es un boost :: any, es decir, se ha perdido el tipo original. Algunos deberían ser lanzados como std :: string, algunos como double, y así sucesivamente.

Así, a cout el valor de la variable, puedo usar esto:

std::cout << vit->first << "="; 
try { std::cout << vit->second.as<double>() << std::endl; 
} catch(...) {/* do nothing */ } 
try { std::cout << vit->second.as<int>() << std::endl; 
} catch(...) {/* do nothing */ } 
try { std::cout << vit->second.as<std::string>() << std::endl; 
} catch(...) {/* do nothing */ } 
try { std::cout << vit->second.as<bool>() << std::endl; 
} catch(...) {/* do nothing */ } 

sólo tengo 4 tipos que utilizo para obtener información del archivo de línea de comandos/config, si he añadido más tipos, tendría que agregar más líneas. Admitiré que esto es un poco feo.

0

Dado que solo va a imprimirlos de todos modos, puede tomar la representación de cadena original al analizar. (Probablemente hay errores de compilación en el código, he arrancado fuera de mi base de código y manojo-un typedefed de las cosas)

std::vector<std::string> GetArgumentList(const std::vector<boost::program_options::option>& raw) 
{ 
    std::vector<std::string> args; 

    BOOST_FOREACH(const boost::program_options::option& option, raw) 
    { 
     if(option.unregistered) continue; // Skipping unknown options 

     if(option.value.empty()) 
      args.push_back("--" + option.string_key)); 
     else 
     { 
      // this loses order of positional options 
      BOOST_FOREACH(const std::string& value, option.value) 
      { 
       args.push_back("--" + option.string_key)); 
       args.push_back(value); 
      } 
     } 
    } 

    return args; 
} 

Uso:

boost::program_options::parsed_options parsed = boost::program_options::command_line_parser(... 

std::vector<std::string> arguments = GetArgumentList(parsed.options); 
// print 
Cuestiones relacionadas