Not able to archive all the data

筅森魡賤 提交于 2020-06-23 12:37:05

问题


I am using boost for serialization of data.

This is how the classes are structured.

1) I have a Stage class This class holds vector data for the director class

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) This is the director class This class holds vector data of weak pointers for the channel class.

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) This is the channel class

Channel class needs to know about the container where it is created and the director where this is stored as a Weak_pointer

1) This class holds a pointer to the Director object.

2) This class holds a pointer to the Container object.

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()

};

////////////////////////////////////////////////////////////////////////////////////

 #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) This is the Container class

1) This is where the Channel is created and saved as a Shared_pointer.

2) The same channel is also saved in the Director class as Weak_Pointer

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()

};

Now when i serialize my data and than serialize it the Director is not able to serialize the 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

回答1:


When you do

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

It adds a copy of dir to the stage::directors` vector.

Later you do

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

This means you point the the variable in main. Which was different from the one pushed into the stage. This is likely not what you wanted.

Comparing:

chn->cont = &cont;

sets a pointer to cont, which is also just a variable in the scope of main. The big difference is that that exact object IS serialized into the archive, so if the archive finds pointers pointing to it it can make the link correctly.

Pointer Conflict

When an object is first deserialized through a pointer it can no longer be deserialized through a reference (because the pointed-to object has already been dynamically allocated).

See for more background inforation: http://www.bnikolic.co.uk/blog/cpp-boost-ser-conflict.html

In your case you have so many cyclic dependencies going on, there is no order in which you can serialize stage and cont so it doesn't lead to pointer conflict.

The easiest way to break the problem is to make vector<Director> into vector<shared_ptr<Director> >. Then you still have to make sure that cont is serialized before stage.

Fixed Demo

Here's a simplified demo that works:

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);
    }
}

Which passes all the asserts and writes a text archive containing:

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


来源:https://stackoverflow.com/questions/62355957/not-able-to-archive-all-the-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!