c++ - 如何为 double 编写一个包装器以与 boost 序列化一起使用?

标签 c++ serialization boost wrapper

在使用文本存档时,boost 序列化库无法正确处理 double 的特殊值。也就是说,尝试反序列化 NaN、+inf 或 -inf 将导致错误(参见例如 this topic )。

因此我想编写一个包装器类/方法,类似于 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)

但是,这更多是反复试验过程的结果,而不是理解包装器工作原理的结果。来自 this part of the doc我得出结论,我需要将该类声明为包装器。但这似乎不起作用。

当我尝试将上述代码与此 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;

}

它失败了。它给了我错误

error: invalid initialization of non-const reference of type ‘Double_wrapper&’ from an rvalue of type ‘Double_wrapper’

为线

ar & Double_wrapper(value);

所以我不知道该怎么办。似乎使用引用不起作用。指针能解决问题吗?这完全有效吗?

任何帮助和/或解释将不胜感激!

我在 Ubuntu 上使用 boost 版本 1.58。


更新 如评论中所述,该代码似乎适用于 vc++。但是,解释 this thread 中的陈述似乎暗示,但实际上并非如此,因为

The reason MSVC might have accepted it, nonetheless, could be because MSVC has an (evil) non-standard extension that extends lifetimes of temporaries, when bound to a non-const reference.

如果我理解正确,在保存并尝试在新实例中反序列化后关闭程序时它应该不再工作。


更新 正如 John Zwinck 所建议的那样,可能有一种解决方法是替换调用

ar & Double_wrapper(value);

通过

Double_wrapper wrapper(value);
ar & wrapper;

但是,这似乎不是用于 boost 序列化的包装器对象的预期行为。此外,(对我而言)不清楚此解决方案是否稳定(我需要它与每个 c++ 编译器一起运行)。

它似乎可以在我的计算机上使用 g++ 5.4.0 和 clang++ 3.8.0 运行。此外,它适用于 vc++ on Rextester ( boost 1.6)。

它在使用 g++ 4.9.3 on rextester 运行时会产生存档异常( boost 1.54)。由于(可能不相关的)链接器错误,我无法在 rextester 上使用 clang 3.7 或在 coliru 上使用 g++ 6.1.0 对其进行测试。

最佳答案

我认为不是这样:

    ar & Double_wrapper(value);

你应该这样做:

    Double_wrapper wrapper(value);
    ar & wrapper;

原因是您的错误消息提示您在需要左值的地方使用了右值。

关于c++ - 如何为 double 编写一个包装器以与 boost 序列化一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39570428/

相关文章:

c++ - 类数据默认初始化

c++ - 当对共享数据的所有引用都被序列化时,为什么函数是线程安全的?

c++ - 从 C++ native Windows 应用程序接收 WebRTC 调用

c# - 使 Visual Studio 设计器忽略公共(public)属性

c++ - 如何复制 boost 信号的槽

c++ - 如何检查 SWIG 接口(interface)文件中的 Lua 版本?

java - Android 异常

c++ - 使用 Boost 和 IOStreams 序列化为静态数据

c++ - 使用 boost::filesystem::path 获取绝对路径

c++ - 为什么 C++ 更喜欢这个模板方法而不是方法重载?