簡體   English   中英

如何編寫雙精度包裝器以與boost序列化一起使用?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM