2012-04-16 73 views
7

我想使用boost :: program_options创建一个可执行文件,可以被称为如下:的boost :: program_options“多态”的说法

./example --nmax=0,10 # nmax is chosen randomly between 0 and 10 
./example --nmax=9  # nmax is set to 9 
./example    # nmax is set to the default value of 10 

什么是实现这一目标的最佳途径,在类型 - 安全的方式,用最少的代码?

+4

使其论点一个字符串,并加少许解析功能。 – 2012-04-16 14:41:01

+0

@KerrekSB:这也是我最初的方法,但我不觉得它非常类型安全,它需要我编写相当一些代码。 – user1202136 2012-04-16 14:41:54

+0

+1因为这是使用'program_options'库的一个常见问题,我不认为在文档 – 2012-04-16 15:54:12

回答

1

我在这里张贴此代码,希望它会证明给别人有用。这是Sam Miller的答案的“模板化”版本。

#ifndef RANDOMCONSTANT_HH 
#define RANDOMCONSTANT_HH 

#include <boost/random.hpp> 

boost::random::mt19937 g_randomConstantPrng(static_cast<unsigned int>(std::time(NULL) + getpid())); 

template<typename T> 
class RandomConstant 
{ 
public: 
    RandomConstant() { /* nothing */ } 
    RandomConstant(T value) : _value(value) { /* nothing */ } 
    RandomConstant(int low, int high) 
    { 
     boost::random::uniform_int_distribution<> dist(low, high); 
     _value = dist(g_randomConstantPrng); 
    } 
    RandomConstant(double low, double high) 
    { 
     boost::random::uniform_real_distribution<> dist(low, high); 
     _value = dist(g_randomConstantPrng); 
    } 
    T value() const { return _value; } 

private: 
    T _value; 
}; 


template<typename T> 
std::ostream& 
operator<<(std::ostream& os, const RandomConstant<T>& foo) 
{ 
    os << foo.value(); 
    return os; 
} 

template<typename T> 
std::istream& 
operator>>(std::istream &is, RandomConstant<T> &foo) 
{ 
    std::string line; 
    std::getline(is, line); 
    if (!is) return is; 

    const std::string::size_type comma = line.find_first_of(','); 
    if (comma != std::string::npos) 
    { 
     const T low = boost::lexical_cast<T>(line.substr(0, comma)); 
     const T high = boost::lexical_cast<T>(line.substr(comma + 1)); 
     foo = RandomConstant<T>(low, high); 
    } 
    else 
    { 
     foo = RandomConstant<T>(boost::lexical_cast<T>(line)); 
    } 

    return is; 
} 

#endif /* RANDOMCONSTANT_HH */ 

使用方法如下:

namespace po = boost::program_options; 
po::options_description desc; 
desc.add_options() 
    ("help", "show help") 
    ("intValue", po::value<RandomConstant<int>>()->default_value(3), "description 1") 
    ("doubleValue", po::value<RandomConstant<double>>()->default_value(1.5), "description 2") 
; 

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

if (vm.count("help")) { 
    std::cerr << desc << std::endl; 
    return EXIT_FAILURE; 
} 

int intValue = vm["intValue"].as<RandomConstant<int>>().value(); 
double doubleValue = vm["doubleValue"].as<RandomConstant<double>>().value(); 
1

库不提供像您所建议的“多态”参数类型。每个参数只有一种类型。如果您希望根据参数的语法使其具有不同的值,则需要自行添加该功能。

简单的方法是按照Kerrek的评论建议并使用一个字符串,然后解析它。它并不需要太多的代码。

另一种方法是使用custom validator。组成一种专用于此格式参数的特殊类型,然后编写一个validate函数,将字符串值转换为您的自定义类型的值。如果验证失败则抛出异常; Program_Options库将它视为任何内置类型的验证失败。我写了an example validator in response to another question

您为此编写的代码与解析命令行后解析字符串的代码基本相同;只是将它构建到参数类型中,还是仅在之后处理它。

4

I would like to use boost::program_options to create an executable which can be called as follows:

program_options库是非常灵活的,这可以很容易地通过流插入和提取运算符编写自己的类中被支持。

#include <iostream> 
#include <limits> 
#include <stdlib.h> 

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


class Max 
{ 
public: 
    Max() : 
     _max(std::numeric_limits<int>::max()) 
    { 

    } 

    Max(
      int max 
     ) : 
     _max(max) 
    { 

    } 

    Max(
      int low, 
      int high 
     ) 
    { 
     int value = rand(); 
     value %= (high - low); 
     value += low; 
     _max = value; 
    } 

    int value() const { return _max; } 

private:  
    int _max; 
}; 

std::ostream& 
operator<<(
     std::ostream& os, 
     const Max& foo 
     ) 
{ 
    os << foo.value(); 
    return os; 
} 

std::istream& 
operator>>(
     std::istream& is, 
     Max& foo 
     ) 
{ 
    std::string line; 
    std::getline(is, line); 
    if (!is) return is; 

    const std::string::size_type comma = line.find_first_of(','); 
    try { 
     if (comma != std::string::npos) { 
      const int low = boost::lexical_cast<int>(line.substr(0, comma)); 
      const int high = boost::lexical_cast<int>(line.substr(comma + 1)); 
      foo = Max(low, high); 
     } else { 
      foo = Max(boost::lexical_cast<int>(line)); 
     } 
    } catch (const boost::bad_lexical_cast& e) { 
     std::cerr << "garbage when convering Max value '" << line << "'" << std::endl; 

     is.setstate(std::ios::failbit); 
    } 

    return is; 
} 

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

    Max nmax; 

    po::options_description options; 
    options.add_options() 
     ("nmax", po::value(&nmax)->default_value(10), "random number range, or value") 
     ("help,h", po::bool_switch(), "help text") 
     ; 

    po::variables_map vm; 
    try { 
     po::command_line_parser cmd_line(argc, argv); 
     cmd_line.options(options); 
     po::store(cmd_line.run(), vm); 
     po::notify(vm); 
    } catch (const boost::program_options::error& e) { 
     std::cerr << e.what() << std::endl; 
     exit(EXIT_FAILURE); 
    } 

    if (vm["help"].as<bool>()) { 
     std::cout << argv[0] << " [OPTIONS]" << std::endl; 
     std::cout << std::endl; 
     std::cout << "OPTIONS:" << std::endl; 
     std::cout << options << std::endl; 
     exit(EXIT_SUCCESS); 
    } 

    std::cout << "random value: " << nmax.value() << std::endl; 
} 

简单的会话

samm:stackoverflow samm$ ./a.out 
random value: 10 
samm:stackoverflow samm$ ./a.out --nmax 55 
random value: 55 
samm:stackoverflow samm$ ./a.out --nmax 10,25 
random value: 17 
samm:stackoverflow samm$ 
+0

中很好地解释了我收到以下错误:/usr/include/boost/lexical_cast.hpp:1147 :61:错误:无法绑定'std :: basic_ostream '左值为'std :: basic_ostream &&' – user1202136 2012-04-16 18:17:10

+0

@user什么平台?我的例子使用boost在Linux和Mac OS X上干净地编译1.49 – 2012-04-16 18:23:40

+0

对以前的评论感到抱歉,我试图复制神庙 - 粘贴,并且它不能很顺利地工作。 – user1202136 2012-04-16 19:14:01

0

你或许可以使用多令牌

po::options_description desc("Allowed options"); 
desc.add_options() 
    ("nmax", po::value< std::vector<float> >()->multitoken()->default_value(10), "description") 
; 

... 

float value; 
if (vm.count["nmax"] == 2) 
    value = random value ... 
else 
    value = vm["nmax"].as< std::vector<float> >()[0]; 
相关问题