2010-11-05 8 views
9

Necesito poder tener boost::program_options analizar una matriz de dobles que se pasan en una línea de comando. Para los dobles positivos, esto no es problema, por supuesto (use multitoken con std::vector<double> en add_options), pero para negativos, sé que estos son argumentos ambiguos.Aceptación de dobles negativos con boost :: program_options

Aquí es una demostración de lo que me gustaría tener en:

 
mycommand --extent -1.0 -2.0 -3.0 1.0 2.0 3.0 --some-other-argument somevalue 

medida debe ser respaldado por una clase Bounds con al menos un constructor que toma en seis T argumentos individuales (en este caso - double).

template <typename T> 
class Bounds 
{ 
public: 
    typedef T value_type; 
    typedef typename std::vector< Range<T> >::size_type size_type; 

    typedef typename std::vector< Range<T> > Ranges; 

    Bounds(T minx, T miny, T minz, 
      T maxx, T maxy, T maxz) 
    { 
     // fill Ranges vector 
    } 

private: 
    Ranges ranges; 
}; 

¿Qué más debo suministrar para apoyar el uso de add_options toman en la clase Bounds? Me gustaría hacer algo similar a esto. ¿Posible?

namespace po = boost::program_options; 
po::options_description options("options"); 

options.add_options() 
    ("extent,e", po::value< Bounds<double> >(), "Extent to clip points to") 

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

po::notify(vm); 

if (vm.count("extent")) 
{ 
    Bounds<double> bounds = vm["extent"].as< Bounds<double> >(); 
    // do other stuff 
} 
+1

Después de hablar con el autor program_options en el IRC, se determinó que esto actualmente no es posible. La solución es deshabilitar opciones cortas o usar comillas. Elegí cotizaciones. –

+0

Lo he informado como ticket, quizás se solucione en un futuro próximo: https://svn.boost.org/trac/boost/ticket/5201 – mloskot

Respuesta

1

El enfoque para el manejo de los números negativos especifica here también podría funcionar para usted.

me analizarlo por el analizador simple

store(command_line_parser(argc, argv).options(commands).run(), vm); 

, pero la solución era utilizar el extendido uno:

parse_command_line 
+0

Tenga en cuenta que esta solución se basa en la desactivación de opciones cortas, por lo que el analizador ignora todo lo que comienza con el guión – mloskot

+0

@mloskot - interesante, ya que OP en ese hilo preguntó específicamente sobre el manejo -ve así como los valores + ve. Esperaba que la discusión se aplicara también aquí, pero no lo intenté. –

+0

Creo que el problema está en manejar los valores negativos como valores y permitir --option y -option al mismo tiempo. Actualmente program_options analiza los valores negativos como nombre de opción (estilo corto) – mloskot

1

La forma más fácil sería para ajustar sus parámetros entre comillas: mycommand --e xtent '-1.0 -2.0 -3.0 1.0 2.0 3.0' --un-otro-argumento algúnvalor

6

El truco es forzar boost para clasificar todos los números como valores de posición (no confundir con positional_options_description. La forma de hacerlo es definir un style_parser y darle a la command_line_parser como extra_style_parser:

#include <boost/program_options/option.hpp> 
    #include <boost/lexical_cast/try_lexical_convert.hpp> 
    #include <boost/program_options/value_semantic.hpp> 

    using po = boost::program_options; 

    std::vector<po::option> ignore_numbers(std::vector<std::string>& args) 
    { 
     std::vector<po::option> result; 
     int pos = 0; 
     while(!args.empty()) { 
      const auto& arg = args[0]; 
      double num; 
      if(boost::conversion::try_lexical_convert(arg, num)) { 
       result.push_back(po::option()); 
       po::option& opt = result.back(); 

       opt.position_key = pos++; 
       opt.value.push_back(arg); 
       opt.original_tokens.push_back(arg); 

       args.erase(args.begin()); 
      } else { 
       break; 
      } 
     } 

     return result; 
    } 

Una vez que lo tienes, esta es la forma en que lo utilice:

po::store(po::command_line_parser(argc, argv) 
     .extra_style_parser(&po::ignore_numbers) 
     .options(commands) 
     .run(), vm); 

Ahora puede utilizar negativo números y argumentos cortos de línea de comando al mismo tiempo.

Sin embargo, todavía hay un problema, no hay manera de restringir el número de tokens que toma cada argumento, lo que puede ser problemático si usa argumentos posicionales.Por ejemplo, algo como esto no funcionará:

foo --coords 1 2 3 4 bar.baz 

Con el fin de solucionar este problema, tendremos que añadir una manera de forzar el número de fichas de un argumento requiere:

template<class T, class charT = char> 
    class bounded_typed_value : public po::typed_value<T, charT> 
    { 
    public: 
     bounded_typed_value(T* store_to) 
      : typed_value<T, charT>(store_to), m_min(-1), m_max(-1) {} 

     unsigned min_tokens() const { 
      if(m_min < 0) { 
       return po::typed_value<T, charT>::min_tokens(); 
      } else { 
       return (unsigned)m_min; 
      } 
     } 

     unsigned max_tokens() const { 
      if(m_max < 0) { 
       return po::typed_value<T, charT>::max_tokens(); 
      } else { 
       return (unsigned)m_max; 
      } 
     } 

     bounded_typed_value* min_tokens(unsigned min_tokens) 
     { 
      if(min_tokens > 1) { 
       po::typed_value<T, charT>::multitoken(); 
      } 

      m_min = min_tokens; 

      return this; 
     } 

     bounded_typed_value* max_tokens(unsigned max_tokens) 
     { 
      if(max_tokens > 1) { 
       po::typed_value<T, charT>::multitoken(); 
      } 

      m_max = max_tokens; 

      return this; 
     } 

     bounded_typed_value* fixed_tokens(unsigned num_tokens) 
     { 
      if(num_tokens > 1) { 
       po::typed_value<T, charT>::multitoken(); 
      } 

      m_min = num_tokens; 
      m_max = num_tokens; 

      return this; 
     } 

    private: 
     int m_min; 
     int m_max; 
    }; 


    template<class T, class charT = char> 
    bounded_typed_value<T, charT>* 
    bounded_value() 
    { 
     return new bounded_typed_value<T, charT>(0); 
    } 

Usted ahora puede poner todo junto así:

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

    boost::program_options::options_description desc; 
    desc.add_options() 
     ("coords,c", boost::program_options::bounded_value<vector<double>>()->fixed_tokens(4), "Bounding box"); 

    po::store(po::command_line_parser(argc, argv) 
     .extra_style_parser(&po::ignore_numbers) 
     .positional(p) 
     .options(commands) 
     .run(), vm); 
+2

Creo que esta debería ser la respuesta aceptada, ya que resuelve el problema y no requiere bloquear opciones cortas. Hay algunos errores como 'option()' debe ser 'po :: option()' a menos que suponga 'using namespace po;' ... – rytis

+0

Gracias, buen truco. Corregido ahora. –

Cuestiones relacionadas