boost::序列化和循环引用反序列化

boost::serialization and cyclic reference deserialization

本文关键字:引用 反序列化 循环 序列化 boost      更新时间:2023-10-16

我有一个树状结构需要序列化。典型结构,每个节点具有parent成员和children向量。 parent是指向类的原始指针,childrenshared_ptr vector。现在看来序列化工作正常,但序列化使parent成员未初始化(指向0xcccccccc0x00000000的指针(。

当实际的父对象尚未完成反序列化时,将加载parent成员,即子对象的parent成员是通过父对象的反序列化请求加载的children。由于这是循环的,我想知道我是否需要采取特殊措施才能使其工作。

感谢您的帮助。

更新:这是我的序列化函数的样子:

template <typename Archive>
void serialize(Archive& archive, GameCore::GameObject& t, const unsigned int version)
{
    archive & boost::serialization::base_object<GameCore::Object>(t);
    archive & boost::serialization::base_object<GameCore::Updatable>(t);
    archive & t.parent;
    archive & t.transform;
    archive & t.components;
    archive & t.children;
}

如果我注释掉archive & t.childrenparent会正确填充。

更新2:好的,我已经设法将其转换为显示问题的最小示例。应编译以下内容:

#include <boostarchivebinary_oarchive.hpp>
#include <boostarchivebinary_iarchive.hpp>
#include <fstream>
#include <memory>
#include <vector>
class A
{
public:
    A() {}
    A(const A& rhs) = delete;
    int someInt = 0;
    A* parent = nullptr;
    std::vector<A*> children;
    template <class Archive>
    void serialize(Archive& archive, const unsigned int version)
    {
        archive & someInt;
        archive & parent;
        int count = children.size();
        archive & count;
        children.resize(count);
        for (int i = 0; i < count; ++i)
        {
            A* ptr = children[i];
            archive & ptr;
            children[i] = ptr;
        }
    }
};

int main()
{
    A* newA = new A();
    newA->someInt = 0;
    A* newPtr = new A();
    newPtr->someInt = 5;
    newPtr->parent = newA;
    newA->children.push_back(newPtr);
    //  Save.
    std::ofstream outputFile("test", std::fstream::out | std::fstream::binary);
    if (outputFile.is_open())
    {
        boost::archive::binary_oarchive outputArchive(outputFile);
        //  Serialize objects.
        outputArchive << newA;
        outputFile.close();
    }
    delete newA;
    delete newPtr;
    A* loadedPtr = nullptr;
    //  Load.
    std::ifstream inputFile("test", std::fstream::binary | std::fstream::in);
    if (inputFile && inputFile.good() && inputFile.is_open())
    {
        boost::archive::binary_iarchive inputArchive(inputFile);
        //  Load objects.
        inputArchive >> loadedPtr;
        inputFile.close();
    }
    return 0;
}

单步执行代码。孩子的parent始终保持为空。

好吧,显然我成了另一个倒霉虫的牺牲品。根据最新的Boost发布页面,Boost 1.55还没有针对VS2013的工作序列化库。谈论浪费的时间...

Visual

Studio 2013/Visual C++ 12 的已知错误

Visual Studio 2013 在发布过程中发布得很晚,所以存在几个 未解决的问题。其中包括:

由于缺少包含,序列化无法编译。

我过去也有同样的问题,我没有从开箱即用中找到可靠的解决方案......但是下面的小技巧工作正常 - 您可以分别指定序列化和反序列化函数(不使用默认模板和 &-运算符(:

//! Serialization
void A::serialize(xml_oarchive& ar, const unsigned int version)
{
   ar << value;
}
//! De-serialization
void A::serialize(xml_iarchive& ar, const unsigned int version)
{
   ar >> value;
}

之后,您可以在反序列化方法中指定还原指向父对象的指针,如下所示:

//! Serialization
void A::serialize(xml_oarchive& ar, const unsigned int version)
{
   ar << children;
}
//! De-serialization
void A::serialize(xml_iarchive& ar, const unsigned int version)
{
   ar >> children;
   for(var child: children)
       child->parent = this;
}

此问题已在开发分支上解决。

请参阅 https://svn.boost.org/trac/boost/ticket/9601

如果您使用Boost Serialization,则所有内容都应该开箱即用。您的类序列化可能看起来像

#include <boost/serialization/vector.hpp>
void serialize (Archive&ar, unsigned int version)
{
     //declare possible derived types of nodes
     ar & parent;
     ar & children;
}

存档将散列指针,因此它不会多次创建每个元素。

一个重要的细节:如果你的节点可以是某种派生类型,你需要教存档在//地方期望什么类型,请参阅有关通过指向基类型的指针序列化派生类型的 Boost 文档中的详细信息。

如果您确定您的树结构是正确的且自包含的(root 具有 NULL 作为父节点,所有其他节点都是其各自"父节点"的"子节点"等(,那么您可以更有效地组织代码:

#include <boost/serialization/vector.hpp>
void serialize (Archive&ar, unsigned int version)
{
     //declare possible derived types of nodes (either here or at archive creation point)        
     ar & children;
     if (typename Archive::is_loading())
     {
          for (auto child : children)
             child->parent = this;
     }
}

(我假设您在构造函数中将"父"设置为 NULL(。

我没有用VS 2013测试这段代码。