2011-03-29 7 views
16

La forma tradicional de indicar el final de las opciones para los programas de línea de comando es con la opción --. ¿Cómo puedo obtener boost :: program_options para reconocer esto como una opción y aceptar el resto de la línea de comando como argumentos posicionales? El siguiente no funciona:Usando '-' como marcador de final de opción con boost :: program_options

namespace po = boost::program_options; 

po::positional_options_description posOpts; 
posOpts.add("keywords", 1); 
posOpts.add("input", 1); 

std::vector<std::string> final_args; 

po::options_description desc("Allowed Options"); 
desc.add_options() 
    ... 
    ("", po::value< std::vector<std::string> >(&final_args)->multitoken(), "end of options") 
    ... 
    ; 

po::command_line_parser(argc, argv).options(desc).positional(posOpts).run(); 

Si reparto foo bar como argumentos, me sale nada en final_args (como se esperaba), sino también cuando doy -- foo bar como argumentos (cuando yo esperaría encontrar final_args[0] == "foo" y final_args[1] == "bar") . Supongo que -- es un argumento largo con la cadena vacía como nombre de argumento. Si, en cambio, se supone que debe interpretarse como un argumento corto, con - como el nombre del argumento, ¿cómo especifico eso? Cambiar la especificación del argumento de "" a ",-" no afecta el resultado, por lo que puedo ver.

¿Cómo se obtiene boost :: program_options para manejar -- correctamente?

Editar: Aquí hay un intento de hacer lo que Tim Sylvester sugerido por la creación de un extra_style_parser:

std::vector<po::option> end_of_opts_parser(std::vector<std::string>& args) { 
    std::vector<po::option> result; 

    std::vector<std::string>::const_iterator i(args.begin()); 
    if (i != args.end() && *i == "--") { 
    for (++i; i != args.end(); ++i) { 
     po::option opt; 
     opt.string_key = "pargs"; 
     opt.original_tokens.push_back(*i); 
     result.push_back(opt); 
    } 

    args.clear(); 
    } 

    return result; 
} 

"pargs" se añadió a las opciones de la siguiente manera:

("pargs", po::value< std::vector<std::string> >(&pargs), "positional arguments") 

La ejecución de este con un -- en la lista de argumentos provoca una excepción required_option. (I obtener resultados similares si en lugar de hacer una po::option para cada arg de salida, que empacar todas en po::option::original_tokens en una po::option.)

+0

lo que seguirá el '--' en la línea de comandos? –

+0

En mi caso particular, será entre cero y dos nombres de archivo, inclusive. No es imposible que tenga un archivo que comience con un guión. – uckelman

Respuesta

4

tenía esta misma pregunta, pero dio para arriba en él.

Creo que la manera de hacer esto es llamar program_options::command_line_parser::extra_style_parser(), pasándole una función que toma un vector de cadena por referencia y devuelve un vector de option s (véase el typedef style_parser en cmdline.hpp).

Su función deberá detectar que el primer token es "-", crear un nuevo objeto option, colocar el resto de los tokens en la entrada en el vector de valor option y vaciar el vector de entrada. Consulte program_options::detail::cmdline::parse_long_option, etc., en libs/program_options/src/cmdline.cpp para tener algo de qué empezar.

Probablemente necesite registrar un valor de opción específico para usar para que pueda encontrar fácilmente este objeto especial option al final del análisis sintáctico y extraer del mismo el conjunto de parámetros adicionales que no sean opciones.

Ojalá pudiera darle algún código pero nunca llegué a hacerlo, terminé simplemente tomando los parámetros adicionales uno por línea en stdin.

edición:

me sentí mal por que apunta en una dirección que no resuelve el problema, por lo que volvieron atrás, y tengo trabajo. El problema es que su entrada de argumento posicional no estaba configurada para aceptar tokens múltiples y no estaba completando el value. El código program_options espera ambos o no funciona.

Aquí está el código completo que funciona para mí:

#include <boost/program_options.hpp> 
#include <iostream> 

namespace po = boost::program_options; 
typedef std::vector<std::string> stringvec; 

std::vector<po::option> end_of_opts_parser(stringvec& args) { 
    std::vector<po::option> result; 
    stringvec::const_iterator i(args.begin()); 
    if (i != args.end() && *i == "--") { 
    for (++i; i != args.end(); ++i) { 
     po::option opt; 
     opt.string_key = "pargs"; 
     opt.value.push_back(*i);  // <== here 
     opt.original_tokens.push_back(*i); 
     result.push_back(opt); 
    } 
    args.clear(); 
    } 
    return result; 
} 

int main(int argc, char* argv[]) 
{ 
    po::options_description desc("Allowed Options"); 
    desc.add_options() 
     ("help,h", "produce help message") 
     ("pargs", po::value<stringvec>()->multitoken(), "positional arguments"); 
    //       and here  ^^^^^^^^^^^^^ 

    po::command_line_parser clparser(argc, argv); 
    clparser.extra_style_parser(end_of_opts_parser); 

    po::variables_map vm; 
    po::store(clparser.options(desc).run(), vm); 
    po::notify(vm); 

    bool const help = !vm["help"].empty(); 
    std::cout << "help = " << help << " - "; 

    // in addition, you don't need to use a separate vector of strings:  
    stringvec const& pargs = vm["pargs"].as<stringvec>(); 
    std::copy(pargs.begin(), pargs.end(), 
     std::ostream_iterator<std::string>(std::cout, ",")); 

    return 0; 
} 

Cuando se ejecuta con -h -- foo bar bazhelp = 1 - foo,bar,baz, se imprime.

+0

Intenté lo que dijiste, sin éxito. Ver mi edición a la pregunta del código. – uckelman

+0

@uckelman Miré el código de Boost un poco más y te faltan dos cosas: 'opt.value.push_back (* i)' y 'po :: value ...-> multitoken()'. Actualizaré mi respuesta. –

+0

Gracias! Descubrí que también funciona para crear solo una 'po :: opción' y push_back() todos los valores restantes, en vez de una' po :: opción' para cada vez que pasa el ciclo. Probablemente sea un poco más eficiente de esa manera. – uckelman

4

Hay una solución alternativa simple e insatisfactoria: antes de entregar argv a command_line_parser, compruebe si -- aparece en él. Si es así, restablezca argc en la posición -- para ocultarlo y los argumentos que lo siguen desde command_line_parser. Luego, cuando termine de analizar, trate los argumentos posicionales después de -- a mano. ¡Blech!

6

estoy un poco confundido porque boost::program_options ya lo hace por sí mismo:

$ ./testprog --the-only-option=23 aa --unknown bb 
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::program_options::unknown_option> >' 
    what(): unknown option unknown 
Aborted 

$ ./testprog --the-only-option=23 -- aa --unknown bb 
the only option: 23 
positional: aa 
positional: --unknown 
positional: bb 

El programa:

#include <boost/program_options.hpp> 

#include <iostream> 
#include <vector> 

using namespace std; 

namespace po = boost::program_options; 

static bool handle_command_line(int argc, char *argv[]) 
{ 
    po::options_description desc("Allowed options"); 
    desc.add_options() 
     ("help", "Describe command line options") 
     ("the-only-option", po::value<string>(), "some option") 
     ; 

    po::options_description hidden; 
    hidden.add_options() 
     ("positional", po::value<vector<string> >()) 
     ; 

    po::options_description all_options; 
    all_options.add(desc).add(hidden); 

    po::positional_options_description p; 
    p.add("positional", -1); 

    po::variables_map vm; 
    po::store(po::command_line_parser(argc, argv). 
       options(all_options).positional(p).run(), vm); 
    po::notify(vm); 

    if (vm.count("help")) { 
     cout << "Usage: " << argv[0] << " [options] [args]" << endl; 
     cout << desc << "\n"; 
     return false; 
    } 

    if (vm.count("the-only-option")) 
     cout << "the only option: " << vm["the-only-option"].as<string>() << endl; 

    if (vm.count("positional")) { 
     const vector<string> &v = vm["positional"].as<vector<string> >(); 
     vector<string>::const_iterator it = v.begin(); 
     for (; it != v.end(); ++it) 
      cout << "positional: " << *it << endl; 
    } 

    return true; 
} 

int main(int argc, char *argv[]) 
{ 
    if (!handle_command_line(argc, argv)) 
     return 1; 
    return 0; 
} 
+0

No creo que esto funcione si ya tiene otras opciones posicionales, al menos no puedo hacerlo funcionar. –

Cuestiones relacionadas