[英]How to write a wrapper for doubles to use with boost serialization?
使用文本存檔時,boost序列化庫無法正確處理雙精度的特殊值。 也就是說,嘗試反序列化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);
}
};
但是,我無法理解包裝器類如何在內部工作。 特別是我不明白,在反序列化時,他們如何設法保持與原始變量的連接(在本例中為值)。
我試着像這樣編寫包裝器:
#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;
}
它失敗。 它給了我錯誤
錯誤:從'Double_wrapper'類型的右值開始無效初始化'Double_wrapper&'類型的非const引用
為線
ar&Double_wrapper(value);
所以我不知道該怎么做。 似乎使用引用不起作用。 指針會起作用嗎? 這有用嗎?
任何幫助和/或解釋將不勝感激!
我在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上的vc ++ (boost 1.6)。
在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;
原因是您的錯誤消息抱怨您正在使用左值而需要左值的事實。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.