2012-09-03 21 views
11

我最近開始使用boost::program_options,發現它非常方便。也就是說,有一件事遺漏了我無法用自己的方式對自己進行編碼:boost :: program_options:迭代並打印所有選項

我想遍歷在boost::program_options::variables_map中收集的所有選項,以便在屏幕上輸出它們。這應該成爲一種便利功能,我可以簡單地調用列出所有已設置的選項,而無需在添加新選項或每個程序時更新該功能。

我知道我可以檢查並輸出單個選項,但正如上面所說,這應該成爲一種不理會實際選項的通用解決方案。我進一步知道我可以迭代variables_map的內容,因爲它只是一個擴展的std::map。然後,我可以檢查存儲的boost::any變量中的類型containd,並使用.as<>將其轉換回適當的類型。但是這意味着要爲每種類型的一種情況編碼一個長開關塊。這對我來說看起來不是很好的編碼風格。

所以問題是,是否有更好的方式來迭代這些選項並輸出它們?

回答

5

這是使用訪客模式的好例子。不幸的是boost::any不支持像boost::variant這樣的訪問者模式。儘管如此,還有一些第三方approaches

另一個可能的想法是使用RTTI:創建映射到類型處理函數的已知類型的type_info的映射。

+0

謝謝你的鏈接和關於RTTI的想法。我希望能夠阻止爲所有支持的類型構建一個結構,如果類型增加,我將不得不管理這些結構,但似乎這是不可能的。 基本上,我想把責任推到類型 - 就像他們支持'operator <<'一切正常,否則編譯應該失敗。 – shiin

5

正如@Rost前面提到的,訪客模式在這裏是一個不錯的選擇。要將其與PO一起使用,您需要爲選項使用通知符,以便在選項已通過的情況下,通知程序將填寫您的boost::variant值集中的條目。該組應該分開存放。之後,您可以迭代您的設置並使用boost::apply_visitor自動處理它們的動作(即打印)。

對遊客來說,從boost::static_visitor<>

繼承其實,我做了遊客和通用的方法使用更廣泛。

我創建了一個class MyOption,其中包含說明boost::variant的值和其他選項(如隱式,默認等)。我使用類似於PO爲其選項(請參閱boost::po::options_add())的方式填充類型爲MyOption的對象向量。在通過std::string()double()boosts::varian t初始化時,您可以填充值的類型以及默認值,隱式等其他內容。

之後,我用訪客模式填充boost::po::options_description容器,因爲boost::po需要自己的結構來解析輸入命令行。在填寫期間,我爲每個選項設置了notifyer - 如果它將通過boost::po將自動填充我的原始對象MyOption。你需要執行po::parsepo::notify。之後,您將可以通過Visitor模式使用已填充的std::vector<MyOption*>,因爲它內部保存了boost :: variant。

什麼是好所有這一切 - 你必須在代碼中只寫一次你的選項類型 - 當填寫你的std::vector<MyOption*>

PS。如果使用這種方法,您將面臨設置notifyer爲無值選項的問題,請參閱此主題以獲得解決方案:boost-program-options: notifier for options with no value

PS2。代碼示例:

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

謝謝您的詳細解釋。這也是一個有趣的解決方案。通過這種方式,我還可以輕鬆地爲幫助輸出添加不同的說明,並簡單列出選項值。 – shiin

0

我今天正在處理這種類型的問題。這是一個老問題,但也許這會幫助那些正在尋找答案的人。

我想出的方法是嘗試一堆作爲< ...>(),然後忽略異常。這不是非常漂亮,但我得到它的工作。

在下面的代碼塊中,vm是來自boost program_options的variables_map。 vit是一個通過vm的迭代器,使它成爲一對std :: string和boost :: program_options :: variable_value,後者是一個boost :: any。我可以用vit-> first打印變量的名稱,但vit-> second不容易輸出,因爲它是boost :: any,即原始類型已經丟失。有些應該轉換爲std :: string,有些轉換爲double,依此類推。

所以,來清點變量的值,我可以用這個:

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 */ } 

我只有4種,我用它來得到命令行/ config文件中的信息,如果我增加了更多的類型,我將不得不添加更多的行。我承認這有點難看。

0

既然你打算把它們打印出來,你可以在解析時抓住原始的字符串表示。 (可能有代碼編譯器錯誤,我撕開它從我的代碼庫和未通過typedef一堆東西的)

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

用法:

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

std::vector<std::string> arguments = GetArgumentList(parsed.options); 
// print 
相關問題