【问题标题】:boost::program_options "polymorphic" argumentboost::program_options “多态”参数
【发布时间】:2012-04-16 14:28:51
【问题描述】:

我想使用 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

用最少的代码以类型安全的方式实现这一目标的最佳方法是什么?

【问题讨论】:

  • 将参数设为字符串并添加一点解析函数。
  • @KerrekSB:这也是我最初的方法,但我觉得它不是非常安全的,它需要我编写相当多的代码。
  • +1,因为这是使用 program_options 库时的常见问题,我认为文档中没有很好地解释

标签: c++ boost boost-program-options


【解决方案1】:

我想使用 boost::program_options 创建一个可执行文件 可以这样调用:

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$ 

【讨论】:

  • 我收到以下错误:/usr/include/boost/lexical_cast.hpp:1147:61: error: cannot bind 'std::basic_ostream' lvalue to 'std:: basic_ostream&&'
  • @user 什么平台?我的示例使用 boost 1.49 在 Linux 和 Mac OS X 上干净地编译
  • 对不起之前的评论,我尝试复制-模板化-粘贴,效果不是很顺利。
【解决方案2】:

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

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

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

您为此编写的代码与您在解析命令行后为解析字符串而编写的代码几乎相同;这只是你是否将它构建到参数类型中,或者只是在之后处理它的问题。

【讨论】:

    【解决方案3】:

    我在这里发布此代码,希望它对某人有用。这是 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();
    

    【讨论】:

      【解决方案4】:

      你也许可以使用多令牌

      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];
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-02
        • 2016-02-22
        • 1970-01-01
        • 2011-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多