如何将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_any
比boost::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 << "
";
}
不幸的是,任何唯一的方法是使用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_each
和mpl::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 组件中打印