c++ - Usar ''-'' como marcador de fin de opciones con boost:: program_options
boost-program-options (3)
La forma tradicional de indicar el final de las opciones para los programas de línea de comandos 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 comandos como argumentos posicionales? Lo siguiente no funciona:
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 doy foo bar
como argumentos, no obtengo nada en final_args
(como se esperaba), sino también cuando doy -- foo bar
como argumentos (cuando esperaría encontrar final_args[0] == "foo"
y final_args[1] == "bar"
). Supongo que aquí es un argumento largo con la cadena vacía como su nombre de argumento. Si, en cambio, se supone que debe interpretarse como un argumento corto, con -
como el nombre del argumento, ¿cómo lo especifico? Cambiar la especificación del argumento de ""
a ",-"
no afecta el resultado, por lo que puedo ver.
¿Cómo se consigue impulsar :: program_options para manejar --
correctamente?
Edición: aquí hay un intento de hacer lo que Tim Sylvester sugirió al crear 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"
a las opciones como esta:
("pargs", po::value< std::vector<std::string> >(&pargs), "positional arguments")
La ejecución de esto con un --
en la lista de argumentos provoca una excepción required_option
. (Obtengo resultados similares si en lugar de hacer una po::option
para cada argumento final, los empaco en po::option::original_tokens
en una po::option
).
Estoy un poco confundido porque boost::program_options
ya lo hace todo 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;
}
Hay una solución simple e insatisfactoria: antes de entregar argv
a command_line_parser
, verifique si --
ocurre en él. Si es así, restablezca argc
a la posición de --
para ocultarlo y los argumentos que se arrastran desde command_line_parser
. Luego, cuando termine de analizar, trate con los argumentos posicionales a mano. Blech!
Tuve esta misma pregunta, pero renuncié a ella.
Creo que la forma de hacer esto es llamar a 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 la option
s (consulte el typedef cmdline.hpp en cmdline.hpp ).
Su función deberá detectar que el primer token es "-", crear un nuevo objeto de option
, colocar el resto de los tokens en la entrada en el vector de valores de la option
y vaciar el vector de entrada. Vea program_options::detail::cmdline::parse_long_option
, etc., en libs/program_options/src/cmdline.cpp
para empezar.
Es probable que deba registrar un valor de opción específico para usar, de modo que pueda encontrar fácilmente este objeto de option
especial al final del análisis y extraer de él el conjunto de parámetros adicionales sin opción.
Desearía poder darte un código, pero nunca logré hacer esto, terminé tomando los parámetros adicionales uno por línea en la entrada estándar.
editar:
Me sentí mal por señalarte en una dirección que no resolvió el problema, así que regresé y lo puse a funcionar. El problema es que su entrada de argumento posicional no se configuró para aceptar múltiples tokens y no se completó 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 baz
se imprime help = 1 - foo,bar,baz,