2010-11-05 10 views
9

Devo essere in grado di avere boost::program_options analizzare una serie di doppi che vengono passati su una riga di comando. Per i doppi positivi, questo non è un problema, ovviamente (usa multitoken con std::vector<double> in add_options), ma per quelli negativi , so che questi sono argomenti ambigui.Accettare i doppi negativi con boost :: program_options

Ecco una dimostrazione di quello che vorrei prendere in:

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

misura deve essere sostenuta da una classe Bounds con almeno un costruttore che prende in sei singoli T argomenti (in questo 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; 
}; 

Che altro devo fornire a sostegno utilizzando add_options prendono nella classe Bounds? Mi piacerebbe fare qualcosa di simile a . Possibile?

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

Dopo aver parlato con l'autore program_options su IRC, è stato determinato che questo al momento non è possibile. La soluzione è disabilitare le opzioni brevi o utilizzare le virgolette. Ho scelto le virgolette. –

+0

L'ho segnalato come ticket, forse sarà risolto in un prossimo futuro: https://svn.boost.org/trac/boost/ticket/5201 – mloskot

risposta

1

L'approccio al trattamento dei numeri negativi specificato here potrebbe anche funzionare per voi.

ero analizzarlo dal semplice parser

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

, ma la soluzione era quella di utilizzare l'esteso uno:

parse_command_line 
+0

Nota questa soluzione si basa sulla disabilitazione di opzioni brevi, quindi il parser ignora tutto ciò che inizia con il trattino – mloskot

+0

@mloskot - interessante, dal momento che OP su quel thread ha chiesto in modo specifico sui valori di gestione -ve e + ve. Speravo che la discussione si applicasse anche qui, ma non l'ho provata. –

+0

Penso che il problema sia con la gestione dei negativi come valori e consentendo --opzione e -opzione allo stesso tempo. Attualmente program_options analizza i valori negativi come nome opzione (stile corto) – mloskot

1

Il modo più semplice sarebbe per racchiudere i parametri tra virgolette: mycommand --e xtent '-1.0 -2.0 -3.0 1.0 2.0 3.0' --some-altro-argomento somevalue

6

Il trucco è forzare boost per classificare tutti i numeri come valori di posizione (da non confondere con positional_options_description. Il modo di fare che è definire una style_parser e dare al command_line_parser come 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 volta che avete, questo è come lo si utilizza:

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

È ora possibile utilizzare negativo numeri e brevi argomenti della riga di comando allo stesso tempo.

Tuttavia, c'è ancora un problema, non c'è modo di limitare il numero di token che ogni argomento assume, il che può essere problematico se si usano argomenti posizionali.Per esempio, qualcosa come questo non funzionerà:

foo --coords 1 2 3 4 bar.baz 

Per risolvere questo problema, avremo bisogno di aggiungere un modo per forzare il numero di token un argomento richiede:

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); 
    } 

È ora è possibile mettere tutto insieme in questo modo:

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

Penso che questa dovrebbe essere la risposta accettata, perché risolve il problema e non richiede di bloccare le opzioni brevi. Ci sono alcuni bug come 'option()' dovrebbe essere 'po :: option()' a meno che non si presuma 'using namespace po;' ... – rytis

+0

Grazie, buona cattura. Risolto ora. –

Problemi correlati