Reputation: 4036
I have a tree-like structure that needs to be serialized. Typical structure, with each node having parent
members and children
vectors. parent
is a raw pointer-to-class, and children
are vector
s of shared_ptr
s. Now it seems that serialization works fine, but de-serialization leaves the parent
members uninitialized (pointers to 0xcccccccc
or 0x00000000
).
The parent
members are being loaded when the actual parent object has not yet finished deserializing, i.e. the child's parent
member is loaded through the deserialization request of the parent's children
. Since this is cyclic I was wondering whether I need to take special measures for it to work.
Thanks for the help.
Update: This is how my serializing function looks like:
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;
}
If I comment out archive & t.children
, parent
gets populated correctly.
Update 2: Ok, I've managed to turn this into a minimal sample that exhibits the problem. The following should compile:
#include <boost\archive\binary_oarchive.hpp>
#include <boost\archive\binary_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;
}
Step through the code. The child's parent
stays null, always.
Upvotes: 8
Views: 1106
Reputation: 1621
This has been resolved on the development branch.
See. https://svn.boost.org/trac/boost/ticket/9601
Upvotes: 1
Reputation: 3103
If you use Boost Serialization everything should work out-of-box. You class serialize can look like
#include <boost/serialization/vector.hpp>
void serialize (Archive&ar, unsigned int version)
{
//declare possible derived types of nodes
ar & parent;
ar & children;
}
Archive will hash pointers so it will not create each element more than once.
One important detail: If your nodes can be of some derived type you need to teach archive what types to expect at // place, see details in boost documentation on serializing derived types through pointers to base type.
If you are sure your tree structure is correct and self-contained (root has NULL as a parent, all other nodes are "children" of their respective "parent", etc.) than you can organize code a little more efficiently:
#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;
}
}
(I assume you set "parent" to NULL in constructor).
I did not test this code with VS 2013.
Upvotes: 0
Reputation: 642
I had the same problem in past, and I did not find solid solution from out-of-the-box.. but the following small hack works fine - you could specify serialization and de-serialization functions separately (not using default template and &-operator):
//! 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;
}
And after that you could specify restoring of a pointer to parent object in deserialization method, like the following:
//! 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;
}
Upvotes: 1
Reputation: 4036
Alright, apparently I fell prey to another unlucky bug. Boost 1.55 doesn't yet have a working serialization library for VS2013, according to the latest Boost release page. Talk about wasted time...
Known Bugs with Visual Studio 2013/Visual C++ 12
Visual Studio 2013 was released quite late in the release process, so there exist several unresolved issues. These include:
Serialization can't compile because of a missing include.
Upvotes: 3