2016-10-08 38 views
5

我想做一個位置,列表程序選項boost_program_options,不允許命名程序選項(如--files)。如何添加描述到boost :: program_options的位置選項?

我有下面的代碼片段:

#include <boost/program_options.hpp> 
#include <iostream> 
#include <string> 
#include <vector> 

namespace po = boost::program_options; 

int main(int argc, const char* argv[]) { 
    po::options_description desc("Allowed options"); 
    desc.add_options()("help", "produce help message") 
    ("files", po::value<std::vector<std::string>>()->required(), "list of files"); 

    po::positional_options_description pos; 
    pos.add("files", -1); 

    po::variables_map vm; 
    try { 
    po::store(po::command_line_parser(argc, argv).options(desc).positional(pos).run(), vm); 
    po::notify(vm); 
    } catch(const po::error& e) { 
    std::cerr << "Couldn't parse command line arguments properly:\n"; 
    std::cerr << e.what() << '\n' << '\n'; 
    std::cerr << desc << '\n'; 
    return 1; 
    } 

    if(vm.count("help") || !vm.count("files")) { 
    std::cout << desc << "\n"; 
    return 1; 
    } 
} 

的問題是,我可以讀文件列表的位置參數列表如下:

./a.out file1 file2 file3 

但遺憾的是這樣的,以及(這我想禁用)

./a.out --files file1 file2 file3 

這個問題也是在yie LDS:

./a.out 
Couldn't parse command line arguments properly: 
the option '--files' is required but missing 

Allowed options: 
    --help    produce help message 
    --files arg   list of files 

所以我期望的情況下會更喜歡(OS類似):

./a.out 
Couldn't parse command line arguments properly: 
[FILES ...] is required but missing 

Allowed options: 
    --help    produce help message 
    --optionx    some random option used in future 
    [FILE ...]   list of files 

之後,我刪除desc.add_option()(...)files選項時,它停止工作,所以我認爲我需要它。

+0

爲什麼你需要刪除用指定參數指定輸入文件的能力?這對任何事情都沒有任何不利影響,所以爲什麼不能使用它來禁用它? –

+0

@DanMašek我相信用戶不太清楚,因爲這允許兩種方式成爲有效的輸入策略(我特別希望只有一種策略並且有幫助支持它) – Patryk

+0

好的。在你看來,我認爲最不具侵略性的解決方案。它仍然允許位置選項分散在整個參數列表中,但是您可以輕鬆地添加更多驗證來限制它。 –

回答

5

至於標題中提出的問題,「如何添加描述以提升:: program_options的位置選項?」,在庫中爲此提供了no functionality。你需要自己處理那部分。

至於問題的主體......這是可能的,但以一個稍微圓潤的方式。

位置選項將每個位置映射到名稱,並且名稱需要存在。從代碼(cmdline.cpp)中我可以看出,unregistered標誌不會被設置爲位置參數。 [1],[2]

所以,做你想做的,我們可以做到以下幾點:

  • 隱藏顯示的幫助了--files選項。您需要自己顯示位置選項的適當幫助,但這與以前沒有什麼不同。
  • 在分析和存儲解析的選項到variables_map之間添加我們自己的驗證。

隱藏--files從幫助

在這裏,我們採取的事實優勢,我們可以使用add(...)成員函數創建複合options_description

po::options_description desc_1; 
// ... 
po::options_description desc_2; 
// ... 
po::options_description desc_composite; 
desc_composite.add(desc_1).add(desc_2); 

因此,我們可以把我們的files選項成隱藏options_description,並創建一個我們將僅用於解析階段的組合。 (見下面的代碼)

防止明確--files

我們需要攔截的解析,並將它們存儲到variables_map之間的選項列表。

run()方法command_line_parser返回basic_parsed_options的一個實例,其成員options保存向量basic_option s。每個解析的參數都有一個元素,任何位置選項都從0開始枚舉,任何非位置選項的位置爲-1。當我們將--files看作顯式(非位置)參數時,我們可以使用它來執行我們自己的驗證並引發錯誤。

源代碼示例

See on Coliru

#include <boost/program_options.hpp> 
#include <iostream> 
#include <string> 
#include <vector> 

namespace po = boost::program_options; 

int main(int argc, const char* argv[]) 
{ 
    std::vector<std::string> file_names; 

    po::options_description desc("Allowed options"); 
    desc.add_options() 
     ("help", "produce help message") 
     ("test", "test option"); 

    std::string const FILES_KEY("files"); 

    // Hide the `files` options in a separate description 
    po::options_description desc_hidden("Hidden options"); 
    desc_hidden.add_options() 
     (FILES_KEY.c_str(), po::value(&file_names)->required(), "list of files"); 

    // This description is used for parsing and validation 
    po::options_description cmdline_options; 
    cmdline_options.add(desc).add(desc_hidden); 

    // And this one to display help 
    po::options_description visible_options; 
    visible_options.add(desc); 

    po::positional_options_description pos; 
    pos.add(FILES_KEY.c_str(), -1); 

    po::variables_map vm; 
    try { 
     // Only parse the options, so we can catch the explicit `--files` 
     auto parsed = po::command_line_parser(argc, argv) 
      .options(cmdline_options) 
      .positional(pos) 
      .run(); 

     // Make sure there were no non-positional `files` options 
     for (auto const& opt : parsed.options) { 
      if ((opt.position_key == -1) && (opt.string_key == FILES_KEY)) { 
       throw po::unknown_option(FILES_KEY); 
      } 
     } 

     po::store(parsed, vm); 
     po::notify(vm); 
    } catch(const po::error& e) { 
     std::cerr << "Couldn't parse command line arguments properly:\n"; 
     std::cerr << e.what() << '\n' << '\n'; 
     std::cerr << visible_options << '\n'; 
     return 1; 
    } 

    if (vm.count("help") || !vm.count("files")) { 
     std::cout << desc << "\n"; 
     return 1; 
    } 

    if (!file_names.empty()) { 
     std::cout << "Files: \n"; 
     for (auto const& file_name : file_names) { 
      std::cout << " * " << file_name << "\n"; 
     } 
    } 
} 

測試輸出

有效選項:

>example a b c --test d e 
Files: 
* a 
* b 
* c 
* d 
* e 

無效選項:

>example a b c --files d e 
Couldn't parse command line arguments properly: 
unrecognised option 'files' 


Allowed options: 
    --help     produce help message 
    --test     test option