2012-09-03 27 views
11

Gần đây tôi đã bắt đầu sử dụng boost::program_options và thấy nó rất thuận tiện. Điều đó nói rằng, có một điều còn thiếu là tôi không thể tự viết mã theo cách tốt:boost :: program_options: lặp lại và in tất cả các tùy chọn

Tôi muốn lặp qua tất cả các tùy chọn đã được thu thập trong boost::program_options::variables_map để xuất chúng trên màn hình. Điều này sẽ trở thành một chức năng thuận tiện, mà tôi có thể chỉ cần gọi để liệt kê tất cả các tùy chọn đã được thiết lập mà không cần cập nhật hàm khi tôi thêm các tùy chọn mới hoặc cho mỗi chương trình.

Tôi biết rằng tôi có thể kiểm tra và xuất các tùy chọn riêng lẻ, nhưng như đã nói ở trên, điều này sẽ trở thành một giải pháp chung không biết gì về các tùy chọn thực tế. Tôi biết thêm rằng tôi có thể lặp lại nội dung của variables_map vì nó chỉ đơn giản là mở rộng std::map. Sau đó tôi có thể kiểm tra loại containd trong biến được lưu trữ boost::any và sử dụng .as<> để chuyển đổi loại trở lại thành loại thích hợp. Nhưng điều này có nghĩa là mã hóa một khối chuyển đổi dài với một trường hợp cho mỗi loại. Và điều này không giống như phong cách mã hóa tốt với tôi.

Vì vậy, câu hỏi đặt ra là, có cách nào tốt hơn để lặp qua các tùy chọn này và xuất chúng không?

Trả lời

5

Đó là một trường hợp tốt để sử dụng mẫu Khách truy cập. Rất tiếc, boost::any không hỗ trợ kiểu khách truy cập như boost::variant. Tuy nhiên có một số bên thứ ba approaches.

Một ý tưởng khác là sử dụng RTTI: tạo bản đồ của type_info của các loại đã biết được ánh xạ tới trình xử lý loại functor.

+0

Cảm ơn bạn đã liên kết và các ý tưởng về RTTI . Tôi đã hy vọng rằng tôi có thể ngăn chặn xây dựng một cấu trúc cho tất cả các loại hỗ trợ mà tôi sẽ phải quản lý nếu các loại tăng, nhưng có vẻ như điều này sẽ không thể. Về cơ bản, tôi muốn chuyển buck sang các loại - như thể chúng hỗ trợ 'toán tử <<' mọi thứ hoạt động tốt, nếu không biên dịch sẽ thất bại. – shiin

5

Như @Rost đã đề cập trước đây, Mô hình khách truy cập là một lựa chọn tốt ở đây. Để sử dụng nó với PO, bạn cần sử dụng trình thông báo cho các tùy chọn của mình theo cách mà nếu tùy chọn thông báo thông báo sẽ điền vào một mục nhập trong bộ giá trị boost::variant của bạn. Bộ nên được lưu trữ riêng. Sau đó, bạn có thể lặp qua tập hợp của mình và tự động xử lý các hành động (tức là in) trên chúng bằng cách sử dụng boost::apply_visitor.

Đối với du khách, kế thừa từ boost::static_visitor<>

Thực ra, tôi làm khách và sử dụng cách tiếp cận chung rộng hơn.

Tôi đã tạo class MyOption giữ mô tả, boost::variant cho giá trị và các tùy chọn khác như ngầm, mặc định, v.v. Tôi điền vào một vectơ của các đối tượng thuộc loại MyOption giống như cách PO làm cho các tùy chọn của chúng (xem boost::po::options_add()) qua mẫu. Trong thời điểm vượt qua std::string() hoặc double() để khởi tạo boosts::varian t, bạn điền vào loại giá trị và các thứ khác như mặc định, ngầm định.

Sau đó, tôi đã sử dụng mẫu Khách truy cập để lấp đầy boost::po::options_description vùng chứa vì boost::po cần cấu trúc riêng để phân tích cú pháp dòng lệnh nhập. Trong quá trình điền, tôi đặt người thông báo cho từng tùy chọn - nếu thông báo đó được thông qua boost::po sẽ tự động điền vào đối tượng gốc của tôi là MyOption.

Tiếp theo, bạn cần thực hiện po::parsepo::notify. Sau đó, bạn sẽ có thể sử dụng đã điền std::vector<MyOption*> qua mẫu Khách truy cập vì nó giữ tăng :: biến thể bên trong.

Điều tốt về tất cả những điều này - bạn chỉ phải viết loại tùy chọn một lần trong mã - khi điền std::vector<MyOption*>.

PS. nếu sử dụng phương pháp này, bạn sẽ gặp phải sự cố khi đặt thông báo cho một tùy chọn không có giá trị, hãy tham khảo chủ đề này để nhận giải pháp: boost-program-options: notifier for options with no value

PS2. Ví dụ về mã:

std::vector<MyOptionDef> options; 
OptionsEasyAdd(options) 
    ("opt1", double(), "description1") 
    ("opt2", std::string(), "description2") 
... 
; 
po::options_descripton boost_descriptions; 
AddDescriptionAndNotifyerForBoostVisitor add_decr_visitor(boost_descriptions); 
// here all notifiers will be set automatically for correct work with each options' value type 
for_each(options.begin(), options.end(), boost::apply_visitor(add_descr_visitor)); 
+0

Cảm ơn bạn đã giải thích kỹ lưỡng. Đây cũng là một giải pháp thú vị. Bằng cách này, tôi cũng có thể dễ dàng thêm các mô tả khác nhau cho đầu ra trợ giúp và khi chỉ liệt kê các giá trị tùy chọn. – shiin

0

Tôi chỉ xử lý loại vấn đề này ngay hôm nay. Đây là một câu hỏi cũ, nhưng có lẽ điều này sẽ giúp những người đang tìm kiếm câu trả lời.

Phương pháp mà tôi đưa ra là thử một nhóm gồm < ...>() và sau đó bỏ qua ngoại lệ. Nó không quá đẹp, nhưng tôi đã làm việc đó.

Trong khối mã bên dưới, vm là một biến_map từ boost program_options. vit là một iterator trên vm, làm cho nó một cặp std :: string và boost :: program_options :: variable_value, sau này là một boost :: any. Tôi có thể in tên của biến với vit-> đầu tiên, nhưng vit-> thứ hai không phải là dễ dàng như vậy để đầu ra bởi vì nó là một tăng :: bất kỳ, tức là loại ban đầu đã bị mất. Một số nên được đúc như là một chuỗi :: std, một số như là một đôi, và như vậy.

Vì vậy, để cout giá trị của biến, tôi có thể sử dụng này:

std::cout << vit->first << "="; 
try { std::cout << vit->second.as<double>() << std::endl; 
} catch(...) {/* do nothing */ } 
try { std::cout << vit->second.as<int>() << std::endl; 
} catch(...) {/* do nothing */ } 
try { std::cout << vit->second.as<std::string>() << std::endl; 
} catch(...) {/* do nothing */ } 
try { std::cout << vit->second.as<bool>() << std::endl; 
} catch(...) {/* do nothing */ } 

tôi chỉ có 4 loại mà tôi sử dụng để lấy thông tin từ tập tin dòng lệnh/config, nếu tôi bổ sung thêm các loại, tôi sẽ phải thêm nhiều dòng hơn. Tôi sẽ thừa nhận rằng điều này là một chút xấu xí.

0

Vì bạn sẽ chỉ in ra, bạn có thể lấy biểu diễn chuỗi gốc khi phân tích cú pháp. (Có khả năng có những sai sót biên dịch trong các mã, tôi tách nó ra khỏi codebase của tôi và bó un-typedefed thứ)

std::vector<std::string> GetArgumentList(const std::vector<boost::program_options::option>& raw) 
{ 
    std::vector<std::string> args; 

    BOOST_FOREACH(const boost::program_options::option& option, raw) 
    { 
     if(option.unregistered) continue; // Skipping unknown options 

     if(option.value.empty()) 
      args.push_back("--" + option.string_key)); 
     else 
     { 
      // this loses order of positional options 
      BOOST_FOREACH(const std::string& value, option.value) 
      { 
       args.push_back("--" + option.string_key)); 
       args.push_back(value); 
      } 
     } 
    } 

    return args; 
} 

Cách sử dụng:

boost::program_options::parsed_options parsed = boost::program_options::command_line_parser(... 

std::vector<std::string> arguments = GetArgumentList(parsed.options); 
// print 
Các vấn đề liên quan