cout map with boost :: any

时间:2016-01-08 09:42:45

标签: c++ boost cout

我有一个可能嵌套的“词典”std::map<std::string, boost::any>(或std::any,如果你愿意的话)。现在,我想显示地图。由于boost::any显然不能很好地与<<发挥作用,所以事情变得有点令人讨厌。到目前为止,我正在检查类型,强制转换并将转换为cout

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子句(例如,floatsize_t,...),这就是为什么我对解决方案不是特别满意。< / p>

有没有办法将上述内容推广到更多类型?

2 个答案:

答案 0 :(得分:2)

你可以做的一件事就是减轻(但不是消除)痛苦,将类型确定逻辑分解为一个支持函数,同时使用静态多态(特别是模板)将动作应用于值... < / p>

#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语句....)

仍然 - 如果您知道类型集,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';
    }
}

如果你有很多操作并希望减少内存使用量,你可以让模板化的构造函数创建并存储一个(abstract-base-class)指针,指向一个特定于静态类型的类,该类派生自一个抽象接口。想要支持:那样你只需要为每个Super_Any对象添加一个指针。

答案 1 :(得分:1)

由于您已经在使用Boost,因此可以考虑使用boost::spirit::hold_any

它已经预先定义了流媒体运营商(operator<<()operator>>())。

只需嵌入类型必须定义相应的运算符,但在您的使用环境中,这似乎是完全安全的。

尽管位于detail命名空间中,hold_any非常普遍且几乎是一个现成的boost:any替代品(例如Type Erasure - Part IVWhy you shouldn’t use boost::any

需要最新版本的Boost(旧版本有broken copy assignment operator)。