如何将boost :: any打印到流中?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何将boost :: any打印到流中?相关的知识,希望对你有一定的参考价值。

我有一张地图std::map<std::string, boost::any>,它来自boost::program_options包。现在我想打印该地图的内容:

for(po::variables_map::const_iterator it = vm.begin(); it != vm.end(); ++it) {
  std::cerr << it->first << ": " << it->second << std::endl;
}

不幸的是,这是不可能的,因为boost::any没有定义operator<<

打印该地图最简单的方法是什么?

我可以为任何自动尝试将每个any强制转换为int,然后是double,然后是string等等,每次忽略错误并尝试强制转换直到转换成功并且我可以打印为指定类型。

但是在Boost中应该有一个更简单的方法吗?我需要像反向lexical_cast ...

答案

你可以用boost::spirit::hold_any代替。它在这里定义:

#include <boost/spirit/home/support/detail/hold_any.hpp>

并与boost::any完全兼容。与boost::any相比,这个类有两个不同之处:

  • 它利用小对象优化习语和其他一些优化技巧,使spirit::hold_anyboost::any更小更快
  • 它定义了流媒体运营商(operator<<()operator>>()),允许无数地输入和输出spirit::hold_any

唯一的限制是你不能输入一个空的spirit::hold_any,但它需要持有一个(可能是默认构造的)实例,该实例是从输入中预期的。

另一答案

如果你可以将boost::any更改为其他类型,你可以使用Boost.TypeErasure。如果您曾想创建类似any的类型,但只支持在编译时支持这些特定操作的类型,那么这只适合您。

#include <boost/type_erasure/operators.hpp>
#include <boost/type_erasure/any.hpp>
#include <boost/mpl/vector.hpp>
#include <random>
#include <iostream>

namespace te = boost::type_erasure;

typedef te::any<boost::mpl::vector<
    te::copy_constructible<>,
    te::destructible<>,
    te::ostreamable<>
>> streamable_any;

int main()
{
    streamable_any i(42);
    streamable_any d(23.5);
    std::mt19937 mt;
    streamable_any r(mt);
    std::cout << i << "
" << d << "
" << r << "
";
}

Live On Coliru

另一答案

不幸的是,任何唯一的方法是使用type()方法来确定any中包含的内容,然后使用any_cast进行转换。显然你必须启用RTTI,但如果你使用任何一个,你可能已经做过了:

for(po::variables_map::const_iterator it = vm.begin(); it != vm.end(); ++it) {
  if(typeid(float) == it->second.type()) {
      std::cerr << it->first << ": " << any_cast<float>(it->second) << std::endl;
  }
  else if(typeid(int) == it->second.type()) {
      std::cerr << it->first << ": " << any_cast<int>(it->second) << std::endl;
  }
  ...
}
另一答案

定义一些辅助函数输出到流:

template<class T>
bool out_to_stream(std::ostream& os, const boost::any& any_value)
{
    try {
        T v = boost::any_cast<T>(any_value);
        os << v;
        return true;
    } catch(boost:: bad_any_cast& e) {
        return false;
    }
}

您可以为某些类型定义特殊格式

template<>
bool out_to_stream<std::string>(std::ostream& os, const boost::any& any_value)
{
    try {
        std::string v(std::move(boost::any_cast<std::string>(any_value)));
        os << "'" << v << "'";
        return true;
    } catch(boost:: bad_any_cast& e) {
        return false;
    }
}

要么

template<>
bool out_to_stream<bool>(std::ostream& os, const boost::any& any_value)
{
    try {
        os << ((boost::any_cast<bool>(any_value))? "yes" : "no");
        return true;
    } catch(boost:: bad_any_cast& e) {
        return false;
    }
}

然后为boost::any定义一个输出运算符,列出要尝试转换和输出的所有类型

std::ostream& operator<<(std::ostream& os, const boost::any& any_value)
{
    //list all types you want to try
    if(!out_to_stream<int>(os, any_value))
    if(!out_to_stream<double>(os, any_value))
    if(!out_to_stream<bool>(os, any_value))
    if(!out_to_stream<std::string>(os, any_value))
        os<<"{unknown}"; // all cast are failed, an unknown type of any
    return os;
}

然后是value_type:

std::ostream& operator<<(std::ostream& os, const boost::program_options::variable_value& cmdline_val)
{
    if(cmdline_val.empty()){
        os << "<empty>";
    } else {
        os<<cmdline_val.value();
        if(cmdline_val.defaulted()) 
            os << "(default)";
    }
    return os;
}
另一答案

我认为你必须涵盖你必须打印的每个可能的对象案例......或者使用boost :: variant。

编辑:对不起,我以为我会写为什么。

我认为这是因为,查看任何源代码,它似乎依赖于您在插入和获取数据时提供类型的事实。插入时,编译器会自动检测数据,因此您不必指定它。但是当你获得数据时,你应该使用any_cast,因为你不确定你得到的数据类型。

如果它以不同的方式工作并且数据类型是肯定的,我认为不需要any_cast :)

相反,variant具有一组有限的可能数据类型,并且此信息在某种程度上已注册,使您能够以通用方式迭代变量容器。

如果你需要这种操作 - 迭代一组通用的值 - 我认为你应该使用variant。

另一答案

尝试使用xany https://sourceforge.net/projects/extendableany/?source=directory xany类允许为任何现有功能添加新方法。顺便提一下,文档中有一个例子可以完全满足您的需求。

另一答案

我没有重新编写我的课程来使用boost::spirit::hold_any,而是创建了一种流式传输boost::any的方法,类似于manifest所建议的,但只是在一个地方。

ostream& operator<<(ostream& _os, const boost::any& _any)
{
  // only define simple type conversions
  if (_any.type() == typeid(int))
    _os << boost::any_cast<int>(_any);

   /*any other types you use...*/
}

相当麻烦,但它允许我在我的代码中的任何地方流式传输boost::any变量。

如何能够从boost::spirit::hold_any构建boost:any

另一答案

其他答案中提出的类型开关列表可以通过使用Boost MPL在类型列表上循环来改进(参见mpl::for_eachmpl::vector的文档)。以下代码为类型列表operator<<中给出的任何boost::any定义了SupportedTypes,否则抛出异常。

#include <stdexcept>
#include <iostream>
#include <string>

#include <cstdint>

#include <boost/any.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/vector.hpp>

class StreamInserter
{
private:
    std::ostream& os_;
    const boost::any &v_;
    mutable bool has_printed_;

public:
    struct UnsupportedType {};

    StreamInserter(std::ostream& os, const boost::any &v)
        : os_(os), v_(v), has_printed_(false) {}

    template <typename T>
    void operator()(const T&) const
    {
        if (!has_printed_ && v_.type() == typeid(T))
        {
            os_ << boost::any_cast<T>(v_);
            has_printed_ = true;
        }
    }

    void operator()(const UnsupportedType&) const
    {
        if (!has_printed_)
            throw std::runtime_error("unsupported type");
    }
};

std::ostream& operator<<(std::ostream& os, const boost::any& v)
{
    typedef boost::mpl::vector<float, double, int8_t, uint8_t, int16_t, uint16_t,
            int32_t, uint32_t, int64_t, uint64_t, std::string, const char*,
            StreamInserter::UnsupportedType> SupportedTypes;
    StreamInserter si(os, v);
    boost::mpl::for_each<SupportedTypes>(si);
    return os;
}

int main(int, char**)
{
    std::cout << boost::any(42.0) << std::endl;
    std::cout << boost::any(42) << std::endl;
    std::cout << boost::any(42UL) << std::endl;
    std::cout << boost::any("42") << std::endl;
    std::cout << boost::any(std::string("42")) << std::endl;
    std::cout << boost::any(bool(42)) << std::endl; // throws exception
}
另一答案

对于这个派对来说有点晚了,但是任何可能感兴趣的人也可以使用std::tuple和类似std::for_each的模板来迭代一个元组。

这是基于ingomueller.net在这个帖子中的答案。

我有一个最近的案例,我创建了一个属性映射(从XML文件读取配置值,主要是基本类型,

以上是关于如何将boost :: any打印到流中?的主要内容,如果未能解决你的问题,请参考以下文章

5hutool实战:IoUtil 流操作工具类(将内容写到流中)

5hutool实战:IoUtil 流操作工具类(将内容写到流中)

如何从 rxjs 流中过滤单个值以在 Angular 2 组件中打印

如何定义 boost::any 运算符 ==

akka 流将 akka-htpp Web 请求调用集成到流中

将 boost 类型擦除类型转换回原始类型给了我 boost::bad_any_cast