2016-01-08 87 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)。