问题
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