【问题标题】:Not able to archive all the data无法归档所有数据
【发布时间】:2020-10-02 23:03:09
【问题描述】:

我正在使用 boost 进行数据序列化。

这就是类的结构。

1) 我有一个 Stage 课程 该类保存director类的矢量数据

class Stage
{
public:
    std::vector<Director> directors;
    void AddDirector(Director dir) { directors.push_back(dir); }
    int GetDirectorSize() { return directors.size(); }
    Director* GetDirector(int number) { return &directors[number]; }

private:
    friend class boost::serialization::access;
    template<typename Archive>
    void save(Archive& ar, const unsigned int version) const {
        ar & directors;
    }
    template<typename Archive>
    void load(Archive& ar, const unsigned int version) {
        ar & directors;
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER()
};

2) 这是导演类 该类保存通道类的弱指针向量数据。

class Director
{
public:
    std::string stdstrName;
    std::vector<std::weak_ptr<Channel>> channels;
    Director() { stdstrName = "NO_NAME"; }
    void SetName(std::string name) { stdstrName = name; }
    std::string GetName() { return stdstrName; }
    void AddChannel(std::weak_ptr<Channel> chn) { channels.push_back(chn); }
    std::shared_ptr<Channel> GetChannel(int number) { return channels[number].lock(); }
    int GetChannelSize() {return channels.size();}
    std::string GetChannelType( int number){
        if (std::shared_ptr<Channel> chn = channels[number].lock())
            return chn->GetChannelType();   
    }

private:
    friend class boost::serialization::access;
    template<typename Archive>
    void save(Archive& ar, const unsigned int version) const {
        ar & stdstrName & channels;
    }
    template<typename Archive>
    void load(Archive& ar, const unsigned int version) {
        ar & stdstrName & channels;
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER()

};

3) 这是通道类

Channel 类需要知道创建它的容器以及 将其存储为 Weak_pointer 的导向器

1) 这个类拥有一个指向 Director 对象的指针。

2) 这个类拥有一个指向 Container 对象的指针。

class Container;
class Director;

class Channel
{
public:
    Director* dir;
    Container* cont;
    std::string stdstrChannelType;
    Channel() { stdstrChannelType = "NO_TYPE"; }
    Channel(std::string type): stdstrChannelType(type){  }
    void SetDirector(Director* director);
    void SetContainer(Container* container);
    std::string GetChannelType() { return  stdstrChannelType;}
    Director* GetDirector() { return dir; }

private:
    friend class boost::serialization::access;
    template<typename Archive>
    void save(Archive& ar, const unsigned int version) const {
        ar & dir & cont & stdstrChannelType;
    }
    template<typename Archive>
    void load(Archive& ar, const unsigned int version) {
        ar & dir & cont & stdstrChannelType;
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER()

};

/////////////////////////////////////// /////////////////////////////////////////p>

 #include "Channel.h"
#include <vector>

class PositionChannel : public Channel
{
public:
    std::vector<int> keyframes;
    PositionChannel() : Channel("POSITION") , keyframes( { 1 , 2, 3 }) {    }

   private:
   friend class boost::serialization::access;

typedef Channel _Super;
template<typename Archive>
void save(Archive& ar, const unsigned int version) const {
    ar & boost::serialization::base_object<_Super>(*this);
    ar & keyframes;
}
template<typename Archive>
void load(Archive& ar, const unsigned int version) {
    ar & keyframes;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()

};

4) 这是容器类

1) 这是创建 Channel 并保存为 Shared_pointer 的位置。

2) 相同的通道也作为Weak_Pointer保存在Director类中

class Container
{
public:
    std::string stdstrName;
    std::vector<std::shared_ptr<Channel>> channel;
    Container() { stdstrName = "cont"; };
    void AddChannel(std::shared_ptr<Channel> chn) 
    { 
        channel.push_back(chn); 
        Director* dir = chn->GetDirector();  // Add the channel to director also
        dir->AddChannel(chn);
    }

private:
    friend class boost::serialization::access;
    template<typename Archive>
    void save(Archive& ar, const unsigned int version) const {
        ar & stdstrName & channel;
    }
    template<typename Archive>
    void load(Archive& ar, const unsigned int version) {
        ar & stdstrName & channel;
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER()

};

现在当我序列化我的数据而不是序列化它时,Director 无法序列化weak_pointer。

Stage stage;
Director dir;
Container cont;
dir.SetName("MAIN");
stage.AddDirector(dir); // Add director to stage
std::shared_ptr<PositionChannel> chn = std::make_shared<PositionChannel>(PositionChannel()); // create a position channel
chn->SetDirector(&dir); // Link the director to channel
chn->SetContainer(&cont); // Link the container to the channel
cont.AddChannel(chn);   // add the channel to the container
std::cout << dir.GetChannelSize() << std::endl; // this gives a value of 1 which is correct
std::ofstream ofs("D://abc.dat");
{
    boost::archive::text_oarchive oa(ofs);
    // write class instance to archive
    oa << stage <<  cont;  // since director is a data element of stage so it should get serialized
}
Stage stage1;
Container cont1;
{
    // create and open an archive for input
    std::ifstream ifs("D://abc.dat");
    boost::archive::text_iarchive ia(ifs);
    // read class state from archive
    ia >> stage1 >> cont1;

}
std::cout << stage1.GetDirectorSize(); // stage has got the director
Director* dir1 = stage1.GetDirector(0);
std::cout << dir1->GetName(); // the director has the correct name
std::cout << dir1->GetChannelSize(); // it should show 1 as the channel size but i am getting 0

【问题讨论】:

  • 问题是什么? “Director 无法序列化weak_pointer”——这是否意味着它无法编译?它做你所期望的吗?会发生什么?
  • @sehe 我把这个问题复杂化了,请看看我的新问题,stackoverflow.com/questions/62367775/…
  • 我同意你把它复杂化了。不是问题,而是代码。也许您应该使用简单的拥有对象,并使用单独的集合/映射建立关系。我觉得你可以在没有单个共享指针或循环依赖的情况下对此进行建模。

标签: c++ serialization boost c++14


【解决方案1】:

当你这样做时

stage.AddDirector(dir); // Add director to stage

它将dir一个副本添加到 stage::directors` 向量中。

稍后再做

chn->SetDirector(&dir); // Link the director to channel

这意味着您将变量指向main。这与被推上舞台的人不同。这可能不是您想要的。

比较:

chn->cont = &cont;

设置一个指向cont的指针,它只是main范围内的一个变量。最大的不同是该对象被序列化到存档中,因此如果存档找到指向它的指针,它可以正确建立链接。

指针冲突

当一个对象第一次通过指针反序列化时,它不能再通过引用反序列化(因为指向的对象已经被动态分配)。

查看更多背景信息:http://www.bnikolic.co.uk/blog/cpp-boost-ser-conflict.html

在你的情况下,你有这么多循环依赖,你可以序列化 stagecont 没有顺序,所以它不会导致指针冲突。

解决问题的最简单方法是将vector&lt;Director&gt; 变成vector&lt;shared_ptr&lt;Director&gt; &gt;。那你还是要确保contstage之前被序列化了。

固定演示

这是一个有效的简化演示:

Live On Coliru

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/weak_ptr.hpp>
#include <boost/serialization/export.hpp>
#include <fstream>

namespace Lib {
    struct Container;
    struct Director;

    struct Channel {
        Director* dir = nullptr;
        Container* cont = nullptr;
        virtual ~Channel() = default;
    };
    struct PositionChannel : Channel {
        std::vector<int> keyframes;
    };
    struct Director {
        std::string name;
        std::vector<std::weak_ptr<Channel>> channels;
    };
    struct Stage {
        std::vector<std::shared_ptr<Director> > directors;
    };
    struct Container {
        std::vector<std::shared_ptr<Channel> > channels;
    };

    template <typename Ar> void serialize(Ar& ar, Channel& o, unsigned) {
        //ar & o.dir & o.cont; // avoid pointer conflict
        ar & o.cont & o.dir;
    }
    template <typename Ar> void serialize(Ar& ar, PositionChannel& o, unsigned) {
        ar & boost::serialization::base_object<Channel>(o)
           & o.keyframes;
    }
    template <typename Ar> void serialize(Ar& ar, Director& o, unsigned) {
        ar & o.name & o.channels;
    }
    template <typename Ar> void serialize(Ar& ar, Stage& o, unsigned) {
        ar & o.directors;
    }
    template <typename Ar> void serialize(Ar& ar, Container& o, unsigned) {
        ar & o.channels;
    }
}

BOOST_CLASS_EXPORT(Lib::Channel)
BOOST_CLASS_EXPORT(Lib::PositionChannel)

int main() {
    using namespace Lib;
    {
        Stage stage;
        Container cont;

        auto dir = std::make_shared<Director>();
        dir->name = "MAIN";
        stage.directors.push_back(dir); // Add director to stage

        auto chn = std::make_shared<PositionChannel>(PositionChannel()); // create a position channel
        chn->dir = dir.get();
        chn->cont = &cont;

        dir->channels.emplace_back(chn); // add the weak ptr

        cont.channels.insert(cont.channels.end(),
            { 
                chn,
                std::make_shared<PositionChannel>(),
                std::make_shared<PositionChannel>(),
                std::make_shared<PositionChannel>(),
            });

        {
            std::ofstream ofs("abc.dat");
            boost::archive::text_oarchive oa(ofs);
            //oa << stage << cont;
            oa << cont << stage;
        }
    }

    {
        std::ifstream ifs("abc.dat");
        boost::archive::text_iarchive ia(ifs);
        Stage stage;
        Container cont;

        //ia >> stage >> cont;
        ia >> cont >> stage;

        assert(cont.channels.size() == 4);

        auto chn = cont.channels.front();
        assert(chn == chn->dir->channels.front().lock());
        assert(chn->cont == &cont);
    }
}

它通过了所有的断言并写入了一个包含以下内容的文本存档:

22 serialization::archive 17 1 0
0 0 0 4 1 0 1 4 20 Lib::PositionChannel 1 0
1 1 0
2 0 0 5 1 0
3 4 MAIN 0 0 1 0 0 0 4 1 0 0 4
4
5 -1 -1 0 0 4
6
7 -1 -1 0 0 4
8
9 -1 -1 0 0 0 0 0 0 1 1 0 1 5 3

【讨论】:

  • 非常感谢您的回答,我知道这是一个很长的问题,再次感谢您宝贵的时间。
  • 没问题。我自己又学到了一些东西(关于指针冲突的部分)。我会认真考虑简化方法。如果您愿意,我们可以找个时间聊聊。
  • 当然是我的荣幸。
  • 请告诉我我们什么时候可以聊天。
  • @Summit 我们可以从这里开始chat.stackoverflow.com/rooms/215943/summit-sehe
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多