2012-04-16 4 views
7

Tôi muốn sử dụng tăng :: program_options để tạo ra một thực thi mà có thể được gọi như sau:boost :: program_options "đa hình" Lập luận

./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 

cách tốt nhất để đạt được điều này là gì, trong một loại cách an toàn, với mã tối thiểu?

+4

Hãy lập luận một chuỗi và thêm một chức năng phân tích ít. –

+0

@KerrekSB: Đó cũng là cách tiếp cận ban đầu của tôi, nhưng tôi không thấy nó rất an toàn và nó đòi hỏi tôi phải viết một số mã. – user1202136

+0

+1 vì đây là một câu hỏi phổ biến khi sử dụng thư viện 'program_options' mà tôi không nghĩ là được giải thích rất rõ trong tài liệu –

Trả lời

1

Tôi gửi bài mã này ở đây, hy vọng nó sẽ hữu ích cho ai đó. Đây là phiên bản "templatized" của câu trả lời của 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 */ 

Được sử dụng như sau:

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

Thư viện không cung cấp loại đối số "đa hình" như bạn đề xuất. Mỗi đối số có chính xác một loại. Nếu bạn muốn làm cho nó có các giá trị khác nhau dựa trên cú pháp của đối số, bạn cần tự thêm chức năng đó.

Cách dễ dàng là làm như bình luận của Kerrek gợi ý và sử dụng chuỗi, sau đó phân tích cú pháp sau đó. Nó không thực sự mất nhiều mã.

Một cách khác là sử dụng custom validator. Tạo nên một kiểu đặc biệt dành riêng cho định dạng đối số này, và sau đó viết một hàm validate để chuyển đổi các giá trị chuỗi thành các giá trị của loại tùy chỉnh của bạn. Ném ngoại lệ nếu xác thực không thành công; thư viện Program_Options sẽ xử lý nó giống như một thất bại xác thực của bất kỳ kiểu tích hợp nào. Tôi đã viết an example validator in response to another question.

Mã bạn sẽ viết cho điều này là khá nhiều cùng một mã bạn muốn viết để phân tích cú pháp chuỗi sau khi phân tích cú pháp dòng lệnh; nó chỉ là vấn đề liệu bạn có xây dựng nó vào kiểu đối số hay chỉ xử lý nó sau đó.

4

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

Thư viện program_options rất linh hoạt, điều này có thể dễ dàng được hỗ trợ bằng cách viết lớp của riêng bạn bằng cách chèn luồng và khai thác.

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

phiên mẫu

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

Tôi nhận được lỗi sau: /usr/include/boost/lexical_cast.hpp:1147 : 61: lỗi: không thể liên kết 'std :: basic_ostream ' lvalue to 'std :: basic_ostream &&' – user1202136

+0

@user nền tảng nào? Ví dụ của tôi biên dịch rõ ràng trên Linux và Mac OS X bằng cách sử dụng tăng 1.49 –

+0

Xin lỗi về nhận xét trước đó, tôi đã cố gắng sao chép-templetize-paste và nó không hoạt động rất suôn sẻ. – user1202136

0

Bạn có thể sử dụng multitoken

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