2016-01-08 86 views
2

我有一個可能嵌套的「字典」std::map<std::string, boost::any>(或std::any,如果需要的話)。現在,我想顯示地圖。由於boost::any顯然與<<沒有很好的搭配,事情變得有點討厭。到目前爲止,我檢查的種類,投它和管道投給coutcout map with boost :: any

for (const auto &p: map) { 
    std::cout << std::string(indent + 2, ' ') << p.first << ": "; 
    if (p.second.type() == typeid(int)) { 
    std::cout << boost::any_cast<int>(p.second); 
    } else if (p.second.type() == typeid(double)) { 
    std::cout << boost::any_cast<double>(p.second); 
    } else if (p.second.type() == typeid(std::string)) { 
    std::cout << boost::any_cast<std::string>(p.second); 
    } else if (p.second.type() == typeid(const char*)) { 
    std::cout << boost::any_cast<const char*>(p.second); 
    } else if (p.second.type() == typeid(std::map<std::string, boost::any>)) { 
    show_map(
     boost::any_cast<std::map<std::string, boost::any>>(p.second), 
     indent + 2 
     ); 
    } else { 
    std::cout << "[unhandled type]"; 
    } 
    std::cout << std::endl; 
} 
std::cout << std::string(indent, ' ') << "}"; 

這印刷品,例如

{ 
    fruit: banana 
    taste: { 
    sweet: 1.0 
    bitter: 0.1 
    } 
} 

不幸的是,這是很難擴展。我必須爲每種類型添加另一個else if子句(例如,float,size_t,...),這就是爲什麼我對解決方案不滿意的原因。

有沒有辦法將以上概括爲更多類型?

+5

那麼不要使用'boost :: any'。如果您想對「可打印」類型進行類型擦除,則需要將打印邏輯粘貼到類型橡皮擦上。我認爲有一個實驗性的「提升」。TypeErasure「(可能不是Boost本身),它允許你爲特定目的合成類型擦除類。 –

+1

你或許可以用'long double','std :: maxint_t'和'std :: maxuint_t'以覆蓋所有內置的類型,但正如Kerrek所說,如果你想要一般的打印,你需要它在你的類型橡皮擦。 –

+0

@MartinBonner:你是什麼意思「逃避」?代碼看起來如何? –

回答

2

你可以做的一件事情是減少(但不是消除)痛苦是將類型確定邏輯分解爲一個支持函數,而使用靜態多態性(特別是模板)來應用於值的動作...

#include <iostream> 
#include <boost/any.hpp> 
#include <string> 

struct Printer 
{ 
    std::ostream& os_; 

    template <typename T> 
    void operator()(const T& t) 
    { 
     os_ << t; 
    } 
}; 

template <typename F> 
void f_any(F& f, const boost::any& a) 
{ 
    if (auto p = boost::any_cast<std::string>(&a)) f(*p); 
    if (auto p = boost::any_cast<double>(&a))  f(*p); 
    if (auto p = boost::any_cast<int>(&a))   f(*p); 
    // whatever handling for unknown types... 
} 

int main() 
{ 
    boost::any anys[] = { std::string("hi"), 3.14159, 27 }; 
    Printer printer{std::cout}; 
    for (const auto& a : anys) 
    { 
     f_any(printer, a); 
     std::cout << '\n'; 
    } 
} 

(只有微幅下挫更多的努力,你可以有特定類型的測試,並派出一個可變參數模板參數包每種類型的完成,從而簡化代碼並維護該列表的麻煩。或者,你可以使用預處理器宏來驅散if-cast/dispatch語句....)

Still - if you kn所有這些類型,boost::variant更合適,並且已經支持類似的操作(請參閱here)。

另一種選擇是「記憶」怎麼辦具體操作 - 如打印 - 當你創建你的類型:

#include <iostream> 
#include <boost/any.hpp> 
#include <string> 
#include <functional> 

struct Super_Any : boost::any 
{ 
    template <typename T> 
    Super_Any(const T& t) 
     : boost::any(t), 
     printer_([](std::ostream& os, const boost::any& a) { os << boost::any_cast<const T&>(a); }) 
    { } 

    std::function<void(std::ostream&, const boost::any&)> printer_; 
}; 

int main() 
{ 
    Super_Any anys[] = { std::string("hi"), 3.14159, 27 }; 
    for (const auto& a : anys) 
    { 
     a.printer_(std::cout, a); 
     std::cout << '\n'; 
    } 
} 

如果你有很多的操作,想減少內存使用,你可以有模板化的構造函數創建並存儲一個(抽象基類)指針,該指針指向一個靜態類型特定的類,該類從具有要支持的操作的抽象接口派生出來:這樣您只需要爲每個對象添加一個指針。

+1

下面是[Boost.TypeErasure](http://www.boost.org)中的相應示例/doc/html/boost_typeerasure/examples.html#boost_typeerasure.examples.printf)。 –

+0

@KerrekSB:cool - 謝謝你。還偶然發現[這個答案](http://stackoverflow.com/a/4985656/410767) ,這是我在最後一段中描述的...... –

+0

是的 - 所有這些方法都需要一個獨立的動態分派*。通過Boost.TypeErasure,您可以創建一個自定義類型,它具有*所有*所需的功能,並且只需要一個動態分派。 –

1

既然您已經在使用Boost,您可以考慮boost::spirit::hold_any

它已經有預定義的流媒體運營商(均爲operator<<()operator>>())。

只要嵌入式必須有相應的運算符定義,但在您的使用上下文中,這似乎是完全安全的。

儘管在detail命名空間之中,hold_any相當廣泛,幾乎一個準備使用的boost:any替代需要(例如Type Erasure - Part IVWhy you shouldn’t use boost::any

加速的最新版本(舊版本有broken copy assignment operator)。