如何编写用于boost序列化的双精度包装器
How to write a wrapper for doubles to use with boost serialization?
boost序列化库在使用文本存档时无法正确处理double的特殊值。也就是说,试图反序列化NaN, +inf或-inf将导致错误(参见本主题)。
因此,我想写一个包装类/方法,类似于make_array或make_binary_object(见boost doc)来处理这些值。我想这样使用它:class MyClass {
public:
double value;
template <class Archive>
void serialize(Archive &ar, const unsigned int){
ar & Double_wrapper(value);
}
};
然而,我不明白包装器类是如何在内部工作的。特别是我不明白,当反序列化时,它们是如何设法保持与原始变量(在本例中为value)的连接的。
我试着这样写包装器:
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/wrapper.hpp>
#include <boost/serialization/tracking.hpp>
#include <limits>
#include <cmath>
class Double_wrapper {
private:
enum double_type {DT_NONE, DT_NAN, DT_INF, DT_NINF};
double& value;
public:
Double_wrapper(double& val):value(val){}
Double_wrapper(Double_wrapper const& rhs):value(rhs.value) {}
private:
friend class boost::serialization::access;
template<class Archive>
void save(Archive & ar, const unsigned int) const {
double_type flag = DT_NONE;
double val = value;
if (!std::isfinite(val)) {
if (std::isnan(val)) {
flag = DT_NAN;
} else if (val > 0) {
flag = DT_INF;
} else {
flag = DT_NINF;
}
val = 0;
}
ar & val;
ar & flag;
}
template<class Archive>
void load(Archive & ar, const unsigned int) const {
double_type flag;
ar & value;
ar & flag;
switch (flag) {
case DT_NONE: break;
case DT_NAN: value = std::numeric_limits<double>::quiet_NaN(); break;
case DT_INF: value = std::numeric_limits<double>::infinity(); break;
case DT_NINF: value = -std::numeric_limits<double>::infinity();
}
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
BOOST_CLASS_IS_WRAPPER(Double_wrapper)
BOOST_CLASS_TRACKING(Double_wrapper, boost::serialization::track_never)
然而,这更多的是一个试错过程的结果,而不是理解包装器是如何工作的。从文档的这一部分我得出结论,我需要将类声明为包装器。但它似乎不起作用。
当我尝试使用上面的代码与这个MWE
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/wrapper.hpp>
#include <boost/serialization/tracking.hpp>
#include <limits>
#include <cmath>
#include <boost/archive/tmpdir.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <fstream>
#include <iostream>
class Double_wrapper {
private:
enum double_type {DT_NONE, DT_NAN, DT_INF, DT_NINF};
double& value;
public:
Double_wrapper(double& val):value(val){}
Double_wrapper(Double_wrapper const& rhs):value(rhs.value) {}
private:
friend class boost::serialization::access;
template<class Archive>
void save(Archive & ar, const unsigned int) const {
double_type flag = DT_NONE;
double val = value;
if (!std::isfinite(val)) {
if (std::isnan(val)) {
flag = DT_NAN;
} else if (val > 0) {
flag = DT_INF;
} else {
flag = DT_NINF;
}
val = 0;
}
ar & val;
ar & flag;
}
template<class Archive>
void load(Archive & ar, const unsigned int) const {
double_type flag;
ar & value;
ar & flag;
switch (flag) {
case DT_NONE: break;
case DT_NAN: value = std::numeric_limits<double>::quiet_NaN(); break;
case DT_INF: value = std::numeric_limits<double>::infinity(); break;
case DT_NINF: value = -std::numeric_limits<double>::infinity();
}
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
BOOST_CLASS_IS_WRAPPER(Double_wrapper)
BOOST_CLASS_TRACKING(Double_wrapper, boost::serialization::track_never)
///////////////////////////////////////////////////////////////////////////////////////
class MyClass {
public:
double value;
template <class Archive>
void serialize(Archive &ar, const unsigned int){
ar & Double_wrapper(value);
}
};
///////////////////////////////////////////////////////////////////////////////////////
int main() {
MyClass tmp;
tmp.value = std::numeric_limits<double>::quiet_NaN();
std::cout << "value=" << tmp.value << std::endl;
std::string filename(boost::archive::tmpdir());
filename += "/tmp.txt";
//Output
std::ofstream ofs(filename.c_str(), std::ios_base::out);
boost::archive::text_oarchive oar(ofs);
oar << tmp;
ofs.close();
//Input
MyClass newtmp;
std::ifstream ifs(filename.c_str(), std::ios_base::in);
boost::archive::text_iarchive iar(ifs);
iar >> newtmp;
std::cout << "value=" << newtmp.value << std::endl;
}
它失败。它给出了错误
错误:类型的非const引用初始化无效从' Double_wrapper '类型的右值中取出' Double_wrapper& '
表示
行基于"增大化现实"技术,
Double_wrapper(价值);
所以我不知道该怎么做。看来使用推荐信不起作用。指针能起作用吗?这真的有效吗?
任何帮助和/或解释将非常感激!
我在Ubuntu上使用boost 1.58版本。
正如注释中提到的,代码似乎可以与vc++一起工作。然而,解释这个线程中所做的陈述似乎表明,它实际上没有,因为
尽管如此,MSVC可能接受它的原因可能是因为MSVC有一个(邪恶的)非标准扩展,当绑定到非const引用时,该扩展可以延长临时对象的生命周期。
如果我理解正确,在保存并尝试在新实例中反序列化后关闭程序时,它应该不再工作。
正如John Zwinck所建议的,可以通过替换调用
来解决这个问题。ar & Double_wrapper(value);
Double_wrapper wrapper(value);
ar & wrapper;
然而,这似乎不是包装器对象用于boost序列化的预期行为。此外,(对我来说)这个解决方案是否稳定(我需要它与每个c++编译器一起运行)还不清楚。
它似乎工作在我的电脑与g++ 5.4.0和clang++ 3.8.0。此外,它还可以在Rextester (boost 1.6)上与vc++一起使用。
在rextester (boost 1.54)上使用g++ 4.9.3运行时会产生一个归档异常。由于(可能不相关的)链接器错误,我无法在rextester上使用clang 3.7或coliru上使用g++ 6.1.0进行测试。
我想与其这样:
ar & Double_wrapper(value);
你应该这样做:
Double_wrapper wrapper(value);
ar & wrapper;
原因是你的错误消息抱怨你在需要左值的地方使用了右值
- 如何防止 c++ 在从浮点型转换为双精度型(不适用于 IO)时添加额外的小数?
- 正在将csv文件读取为双精度矢量
- 我可以信任表示整数的浮点或双精度来保持精度吗
- 如何在C++中的同一函数中使用字符串和双精度
- 特征::矩阵<双精度,1,3> 结构类型函数中的返回类型函数
- 检查是否以特定精度给出双精度
- 转换函数,将 std::数组的双精度作为参数或双精度作为参数单独转换
- C 字符串返回字符串的整数/双精度/长整型值
- 为什么将双精度转换为 int 似乎在第 16 位数字之后将其四舍五入?
- 如何使双精度值的 C++ 和 C# 中的结果相同
- 使用浮点数和双精度数的非常小数字的数学
- 使用 Xcode 将双精度存储在数组C++中
- 在 C++ 中将双精度变量写入二进制文件
- 如何从字符串转换为双精度*
- 为什么我的数组双精度函数不起作用?
- 高精度双精度的 Sprintf 格式化问题
- C++ cout 将双精度对齐到精度 2 并正确对齐
- 将指针数组分配给双精度
- SIMD与包装和标量双精度的区别
- 如何编写用于boost序列化的双精度包装器