提升非默认可构造类型的序列化

时间:2015-11-03 17:35:10

标签: c++ serialization boost

我正在序列化一个具有boost序列化1.40的非默认构造函数的类。让我们说

typedef struct foo {
    foo(int b) : a(b) {}
    int a;
} foo;

我想:

namespace boost {
namespace serialization {

template<class Archive>
void save_construct_data(Archive& archive, const foo* f, const unsigned int version)
{
    archive & foo->a;
}

template<class Archive>
void load_construct_data(Archive& archive, foo* f, const unsigned int version)
{
    int a;
    archive & a;
    ::new(f)foo(a);
}

template<class Archive>
void save(Archive& archive, const foo& label, const unsigned int version)
{
    ;
}

template<class Archive>
void load(Archive& archive, foo& f, const unsigned int version)
{
    ;
}

template<class Archive>
void serialize(Archive& archive, foo& f, const unsigned int version)
{
    boost::serialization::split_free(archive, f, version);
}
} }

这是(de)序列化对应该实现的类的引用的方式吗?虽然我已经找到(load)save_construct_data用于(de)序列化指向非默认可构造类的指针:

http://www.boost.org/doc/libs/1_40_0/libs/serialization/doc/serialization.html#constructors

我还没有找到如何处理对这些类的引用。

1 个答案:

答案 0 :(得分:2)

文档说明原因:

  

References

     

包含引用成员的类通常需要非默认值   构造函数作为引用只能在构造实例时设置。

     

如果班级有上一节的例子稍微复杂一点   参考成员。这提出了对象的方式和位置的问题   被引用被存储以及它们是如何被创建的。还有   关于引用多态基类的问题。基本上,这些都是   关于指针出现的相同问题。

     

这并不奇怪,因为引用实际上是一种特殊的指针。 我们   通过序列化引用来解决这些问题,就像它们一样   指针。

框架不可能找出引用的生命周期/所有权语义。引用需要手动“引导”:您需要确定谁拥有实例并将任何重要引用序列化为指针。

这有很多问题,序列化引用 [1] 并不是一个好主意。主要是因为参考不能重新安排。

这意味着参考必须在施工时受到约束,对(反)序列化订单提出严格要求。

当引用“逻辑上不属于归档的”外部“实体(例如全局配置,父节点和其他簿记开销)时,引用通常是”正常的“。但在这种情况下,您希望独立于归档数据来实现参考。

每当情况不是这样时,请重新考虑您的设计。

  

注意所引用的文档继续显示如何强制反序列化引用的示例,这使得所有权/生命周期的谜语完全打开,并且正如所写的那样保证内存泄漏。

     

对于非基本类型,您将在单个存档中获得对象跟踪(因此,别名引用的重复数据删除)

[1] 我认为在班上有参考成员很少是个好主意

更新

<强> Live On Coliru

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/ptr_container/serialize_ptr_vector.hpp>
#include <set>
#include <iostream>

struct foo {
    foo(int b) : a(b) {}
    int a;
    int b = 42;
};

struct refholder {
    foo& _ref;
    refholder(foo& ref) : _ref(ref) {}
};

namespace {
    using FooHandle = std::unique_ptr<foo>;
    static std::set<FooHandle> _instances;

    FooHandle const& register_instance(foo* raw) {
        /* manage lifetime externally, ignore dupes because object tracking can
         * lead to same pointer used >once
         */
        auto existing = std::find_if(_instances.begin(), _instances.end(),
                [raw](FooHandle const& p) { return raw == p.get(); });

        if (existing == _instances.end())
            return *_instances.insert(std::unique_ptr<foo>(raw)).first;
        else
            return *existing;
    }
}

namespace boost {
namespace serialization {

    /// {{{ foo serialization
    template <class Archive> void save_construct_data(Archive &ar, const foo *f, const unsigned int /*version*/) {
        ar & f->a;
    }

    template <class Archive> void load_construct_data(Archive &ar, foo *f, const unsigned int /*version*/) {
        int a;
        ar &a;
        ::new (f) foo(a);
    }

    template <class Archive> void serialize(Archive &ar, foo &f, const unsigned int /*version*/) { ar & f.b; }
    // }}} foo serialization

    /// {{{ refholder serialization
    template <class Archive> void save_construct_data(Archive &ar, const refholder *rh, const unsigned int /*version*/) {
        foo* external = &rh->_ref;
        ar & external;
    }

    template <class Archive> void load_construct_data(Archive &ar, refholder *rh, const unsigned int /*version*/) {
        foo* external = nullptr;
        ar & external;

        register_instance(external);

        ::new (rh) refholder(*external); // pass the dereferenced external pointer.
    }

    template <class Archive> void serialize(Archive &, refholder&, const unsigned int /*version*/) {
    }
    // }}} refholder serialization
}
}

#include <sstream>

int main() {

    std::stringstream ss;
    using data_t = boost::ptr_vector<refholder>;

    {
        boost::archive::text_oarchive oa(ss);

        foo shared1(7), shared2(77);

        data_t data;
        data.push_back(new refholder{shared1}); data.push_back(new refholder{shared1}); data.push_back(new refholder{shared1});
        data.push_back(new refholder{shared2}); data.push_back(new refholder{shared2});

        oa << data;
    }

    std::cout << ss.str();

    {
        assert(_instances.empty());

        boost::archive::text_iarchive ia(ss);

        data_t data;
        ia >> data;

        std::cout << "_instances.size(): " << _instances.size() << "\n";
        assert(_instances.size() == 2); // two unique instances
    }

    // _instances will be destructed, leading to leak-free

}

打印

22 serialization::archive 13 0 0 5 1 1 0
0 2 1 0
1 7 42 1
2 2 1 1
3 2 1 1
4 2
5 77 42 1
6 2 5
_instances.size(): 2

另见这个较旧的答案: