2010-06-17 4 views
2

Si ma ligne de commande est:peut augmenter Program_options arguments séparés par des virgules pour séparer les valeurs

> prog --mylist=a,b,c 

peut program_options de Boost être configuré pour voir trois valeurs d'arguments distincts pour l'argument mylist? J'ai configuré program_options comme:

namespace po = boost::program_options; 
po::options_description opts("blah") 

opts.add_options() 
    ("mylist", std::vector<std::string>>()->multitoken, "description"); 

po::variables_map vm; 
po::store(po::parse_command_line(argc, argv, opts), vm); 
po::notify(vm); 

Quand je vérifie la valeur de l'argument mylist, je vois une valeur a,b,c. J'aimerais voir trois valeurs distinctes, séparées par des virgules. Cela fonctionne bien si je spécifie la ligne de commande:

> prog --mylist=a b c 

ou

> prog --mylist=a --mylist=b --mylist=c 

est-il un moyen de configurer program_options afin qu'il voit a,b,c que trois valeurs qui doivent chacun être insérés dans le vecteur, plutôt qu'un? J'utilise boost 1.41, g ++ 4.5.0 20100520, et j'ai activé les extensions expérimentales C++ 0x.

EDIT:

Les travaux de solution acceptée mais finit par être plus compliqué, l'OMI, que seulement itérer un vecteur et diviser les valeurs manuellement. À la fin, j'ai pris la suggestion de James McNellis et l'ai mise en place de cette façon. Sa solution n'a pas été soumise comme réponse, cependant, j'ai donc accepté l'autre solution correcte de hkaiser. Les deux ont fonctionné, mais la segmentation manuelle est plus claire.

+1

Si rien d'autre, vous pouvez utiliser 'boost :: tokenizer' pour marquer les valeurs séparées par des virgules. –

+0

C'est probablement la chose la plus facile à faire. Juste post-traiter l'argument et gérer le cas où une virgule apparaît. –

Répondre

2

Vous pouvez enregistrer un validateur personnalisé pour votre option:

namespace po = boost::program_options; 

struct mylist_option 
{ 
    // values specified with --mylist will be stored here 
    vector<std::string> values; 

    // Function which validates additional tokens from command line. 
    static void 
    validate(boost::any &v, std::vector<std::string> const &tokens) 
    { 
     if (v.empty()) 
      v = boost::any(mylist_option()); 

     mylist_option *p = boost::any_cast<mylist_option>(&v); 
     BOOST_ASSERT(p); 

     boost::char_separator<char> sep(","); 
     BOOST_FOREACH(std::string const& t, tokens) 
     { 
      if (t.find(",")) { 
       // tokenize values and push them back onto p->values 
       boost::tokenizer<boost::char_separator<char> > tok(t, sep); 
       std::copy(tok.begin(), tok.end(), 
        std::back_inserter(p->values)); 
      } 
      else { 
       // store value as is 
       p->values.push_back(t); 
      } 
     } 
    } 
}; 

qui peut ensuite être utilisé comme:

opts.add_options()     
    ("mylist", po::value<mylist_option>()->multitoken(), "description"); 

et:

if (vm.count("mylist")) 
{ 
    // vm["mylist"].as<mylist_option>().values will hold the value specified 
    // using --mylist 
} 
+0

Cela a fonctionné, avec quelques modifications. J'ai dû extraire la fonction de validation de la structure et la surcharger en fonction de la documentation.Je déteste avoir à créer un faux-type pour faire ce que je veux, mais c'est la vie. Au final, je viens juste de parcourir le vecteur et de marquer les valeurs w/une simple boucle. C'est environ 50% moins de code que le validateur personnalisé. Néanmoins, j'accepte cette réponse car elle fonctionne et est la seule réponse correcte soumise. –

+1

pour une raison quelconque ce code ne compile plus pour moi (boost 1.55) – malat

+0

je l'ai résolu: http://stackoverflow.com/questions/26389297/how-to-parse-comma-separated-values-with-boostprogram -options – 4ntoine

2

Je n'ai pas essayé de le faire moi-même, mais vous pourriez éventuellement utiliser la même approche que dans l'exemple custom_syntax.cpp fourni avec program_options, pour écrire votre propre analyseur que vous pouvez fournir en tant qu'analyseur supplémentaire. Il y a un peu d'info here avec un petit exemple. Ensuite, vous pouvez soit combiner cela avec la suggestion de James d'utiliser boost :: tokenizer, ou simplement suivre sa suggestion.

+0

Je ne pense pas que cela fonctionnera. Il semble appeler seulement l'analyseur pour les arguments, pas leurs valeurs. –

+0

Je le reprends, il l'appelle pour chaque jeton, pas pour chaque argument. J'expérimenterai un peu plus. –

+0

@lrm: Désolé, je ne peux pas être plus utile, je n'ai pas beaucoup utilisé les options de programme, mais dites-nous comment ça se passe si vous décidez d'aller avec cela, plutôt que de simplement marquer la chaîne. – Jacob

2

ici est ce que je me sers en ce moment:

template<typename T, int N> class mytype; 
template<typename T, int N> std::istream& operator>> (std::istream& is, mytype<T,N>& rhs); 
template<typename T, int N> std::ostream& operator<< (std::ostream& os, const mytype<T,N>& rhs); 
template < typename T, int N > 
struct mytype 
{ 
    T values[N]; 
    friend std::istream& operator>> <>(std::istream &is, mytype<T,N> &val); 
    friend std::ostream& operator<< <>(std::ostream &os, const mytype<T,N> &val); 
}; 
template<typename T, int N> 
inline std::istream& operator>>(std::istream &is, mytype<T,N> &val) 
{ 
    for(int i = 0; i < N; ++i) 
    { 
    if(i) 
     if (is.peek() == ',') 
     is.ignore(); 
    is >> val.values[i]; 
    } 
    return is; 
} 
template<typename T, int N> 
inline std::ostream& operator<<(std::ostream &os, const mytype<T,N> &val) 
{ 
    for(int i = 0; i < N; ++i) 
    { 
    if(i) os << ','; 
    os << val.values[i]; 
    } 
    return os; 
} 

int main(int argc, char *argv[]) 
{ 
    namespace po = boost::program_options; 

    typedef mytype<int,2> mytype; // let's test with 2 int 
    mytype my; 
    try 
    { 
    po::options_description desc("the desc"); 
    desc.add_options() 
     ("mylist", po::value<mytype>(&my), "mylist desc") 
     ; 

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

    if (vm.count("mylist")) 
     { 
     const mytype ret = vm["mylist"].as<mytype >(); 
     std::cerr << "mylist: " << ret << " or: " << my << std::endl; 
     } 
    } 
    catch(std::exception& e) 
    { 
    std::cout << e.what() << "\n"; 
    }  
    return 0; 
} 
Questions connexes