I am trying to work with boost::serialisation for saving and loading some objects.
So far from the boost::tutorial I have managed to do things work for all the different stl stuff (vectors, pairs, lists etc), for derived classes, for boost multi-arrays and other things, but I am stuck trying to work around how to serialize a boost::any vector.
Let me say in advance that I found and checked in the forum some posts for boost::varian serialization and even one for boost::any (which even has an almost identical title) but yet again I didn't manage to solve my problems.
So let me go with a small example:
Say I have this class:
class class_2
{
private:
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & degrees;
ar & minutes;
ar & seconds;
for( std::vector<boost::any>::iterator it = v_any.begin() ; it != v_any.end() ; ++it ) {
//Trying to cast all the possible types that may enter the
//boost any vector, for the sake of this example let's just
//say that we will only pass a second class called name class_1
//to the boost::any vector and we only have to cast this class
if (it->type() == typeid(class_1)){
class_1 lan = boost::any_cast< class_1 > (*it );
ar & (lan);
}
}// for boost::any*/
} //serialization
public:
int degrees;
int minutes;
float seconds;
class_2(){};
class_2(int d, int m, float s) :
degrees(d), minutes(m), seconds(s)
{}
std::vector<boost::any> v_any;
};
And to be more precise the class_1 that we expect for this small example to exist inside the boost::any vector is the following class:
class class_1
{
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & a;
}
public:
class_1(){};
int a;
};
So when I compile the above code with a main function where I save and load an object of class_2 that containts inside the boost::any vector an object of class_1 everything compiles and even runs:
int main() {
class_1 alpha;
class_2 beta;
alpha.a=5;
beta.v_any.push_back(alpha);
std::ofstream ofs("file");
// save data to archive
{
boost::archive::text_oarchive oa(ofs);
// write class instance to archive
oa << beta;
// archive and stream closed when destructors are called
}
// ... some time later restore the class instance to its orginal state
class_2 newclass;
{
// create and open an archive for input
std::ifstream ifs("file");
boost::archive::text_iarchive ia(ifs);
// read class state from archive
ia >> newclass;
// archive and stream closed when destructors are called
}
return 0;
}
Yet again the loaded newclass object has an empty boost::any vector with nothing saved inside.
So my question is what am I doing wrong in the above code and what should I change in order to serialize the boost::any vector correctly..?
Any help/suggestion would be very appreciated.
Related
I created an output text_archive and I restored it using binary archive and obviously, got some issues.
Can I know somehow the kind of archive, so that I could possibly use the appropriate code for binary/xml/text archive.
class Info
{
private:
// Allow serialization to access non-public data members.
friend class boost::serialization::access;
// Serialize the std::vector member of Info
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & filenames;
}
std::vector<std::string> filenames;
};
int main(int argc, char** argv)
{
Info info;
// Save filename data contained in Info object
{
// Create an output archive
std::ofstream ofs( "store.dat" );
**boost::archive::text_oarchive ar(ofs);**
ar & info;
}
// Restore from saved data and print to verify contents
Info restored_info;
{
// Create and input archive
std::ifstream ifs( "store.dat" );
**boost::archive::binary_iarchive ar(ifs);**
// Load the data
ar & restored_info;
}
return 0;
}
You can use first two bytes to specific type, let's say
00 for binary
01 for xml
10 for text
and the reset of the content is for data itself
I need to write a class to a binary file, and then I need to read it back.
I have Triangle and BinaryFile classes, and some other classes. I am not sure if I am writing incorrectly or reading incorrectly. An error occurs when reading. After debugging, I think that it gets inappropriate data for my private variables. I will be very glad if someone can give me some advice on how to make it work properly.
I wasn't sure if I should paste the whole code or not, so I will give you a short snippet of code. Just in case, here is a download link for my source code:
https://my.pcloud.com/publink/show?code=XZJ7CYZbsLWLglqV5p83csijcEUTFqqpM3k
I am a newbie in programming and I don't speak English very well, so I apologize in advance for my mistakes.
class Point
{
private:
int x;
int y;
};
class Figure
{
private:
string name;
string type;
};
class Triangle: public Figure
{
private:
Point p1, p2, p3;
};
class BinaryFile
{
private:
string FileName;
fstream File;
public:
//...
void AddNewFigure(istream& stream)
{
File.open(this->FileName, ios::binary | ios::app);
if(!this->File)
{
cerr<<"File error <"<<this->FileName<<">\n";
exit(1);
}
Triangle fig;
fig.MakeNewFigure(stream);
File.write((char*)&fig, sizeof(Triangle));
File.close();
}
Triangle GetTriangle()
{
Triangle trig;
Point p;
string str(""); int x(0);
File.open(this->FileName, ios::binary | ios::in);
if(!this->File)
{
cerr<<"File error <"<<this->FileName<<">\n";
exit(1);
}
File.read((char*)&trig, sizeof(Triangle));
File.close();
return trig;
}
};
The answer depends on whether you are just doing this to learn how files work or whether saving to the file is just incidental and you don't care how it works.
If you just want to get the stuff to save and restore and you don't care how it works then use a third party library. There are many many of them.
If you want to learn how to read and write things to files then you will need to make your own read and write functions. I have made a sample program that will explain how it works:
#include <string>
#include <fstream>
#include <iostream>
class Point
{
private:
int x;
int y;
public:
Point():x(0),y(0){}
Point(int x,int y):x(x),y(y){}
void write(std::ostream& f)
{
// We can just write out the bytes for x and y because
// they are primitive types stored in the class
f.write( (char*)&x, sizeof(x) );
f.write( (char*)&y, sizeof(y) );
}
void read(std::istream& f)
{
// We can just read the bytes directly into x and y because
// they are primitive types stored in the class
f.read( (char*)&x, sizeof(x) );
f.read( (char*)&y, sizeof(y) );
}
};
class Figure
{
private:
std::string name;
std::string type;
public:
Figure(){}
Figure(std::string name,std::string type):name(name),type(type){}
void write(std::ostream& f)
{
size_t size;
// we need to store the data from the string along with the size
// because to restore it we need to temporarily read it somewhere
// before storing it in the std::string (istream::read() doesn't
// read directly to std::string)
size = name.size();
f.write( (char*)&size, sizeof(size_t) );
f.write( (char*)name.c_str(), size );
size = type.size();
f.write( (char*)&size, sizeof(size_t) );
f.write( (char*)type.c_str(), size );
}
void read(std::istream& f)
{
size_t size;
char *data;
// when we read the string data we need somewhere to store it
// because we std::string isn't a primitive type. So we read
// the size, allocate an array, read the data into the array,
// load the std::string, and delete the array
f.read( (char*)&size, sizeof(size) );
data = new char[size+1];
f.read( data, size );
data[size]='\0';
name = data;
delete data;
f.read( (char*)&size, sizeof(size) );
data = new char[size+1];
f.read( data, size );
data[size]='\0';
type = data;
delete data;
}
};
class Triangle: public Figure
{
private:
Point p1, p2, p3;
public:
Triangle(){}
Triangle(Point x,Point y,Point z,Figure f):p1(x),p2(y),p3(z),Figure(f){}
void write(std::ostream& f)
{
// First write the base class then write the members of this class
Figure::write(f);
p1.write(f);
p2.write(f);
p3.write(f);
}
void read(std::istream& f)
{
// First read the base class then read the members of this class
Figure::read(f);
p1.read(f);
p2.read(f);
p3.read(f);
}
};
class BinaryFile
{
private:
std::string FileName;
std::fstream File;
public:
BinaryFile(std::string FileName) : FileName(FileName) {};
void WriteTriangle()
{
File.open(FileName, std::ios::binary | std::ios::out);
if(!File)
{
std::cerr<<"File error <"<<FileName<<">\n";
exit(1);
}
Triangle trig({1,2},{3,4},{5,6},{"name","type"}); // something new
trig.write(File);
File.close();
}
Triangle ReadTriangle()
{
File.open(FileName, std::ios::binary | std::ios::in);
if(!File)
{
std::cerr<<"File error <"<<FileName<<">\n";
exit(1);
}
Triangle trig; // default values
trig.read(File);
File.close();
return trig;
}
};
main()
{
BinaryFile bin("file.bin");
bin.WriteTriangle();
Triangle trig = bin.ReadTriangle();
// at this point trig has the values we stored
return 0;
}
It's not easy to reproduce the error, due to your large source code and missing data file. But a quick inspection shows that you read and write the binary data using bloc operations:
Triangle trig;
...
File.read((char*)&trig, sizeof(Triangle));
Unfortunately this kind of approach only works if the object you want to save/load is of a class that is trivially copyable, as the following code will demonstrate:
if (is_trivially_copyable<Triangle>::value)
cout << "Triangle is trivially copyable" << endl;
else cout << "Triangle is not trivially copyable" << endl;
So you'll have to serialize the object content writing field by field instead of using a bloc operation. This FAQ on serialization should help you to consider the alternatives.
What you are looking for is to serialize your classes/data that should be saved to file. There are several libraries that has been optimized regarding time and memory consumption for this. Would you mind using a 3rd party library?
If not, have a look at for example boost serialization, cereal or maybe even Google's ProtoBuf. I recon Cereal is a good start if you are using C++11.
If you'd like to write your own serialization you'd have to consider that for every object that has a dynamic size (such as a string), you will also need to save the object's size to the file. For more info please have a look here:
https://stackoverflow.com/a/11003590/5874704
I have two classes that will represent two very simple databases, and each has a "Save" function which will write what's in the class to a file. Since the code within the "Save" function is very similar, I was wondering if I could factor it out.
One of my colleagues said this might be possible with inheritance and/or metadata, so I tried looking into it myself with Google. However, I couldn't find anything that was helpful and am still unsure if what I want to do is even possible.
If it's possible to factor out, then I think I'd need to have another class or function know about each class's types and iterate through them somehow (metadata?). It would check the type of every data, and depending on what the type is, it would make sure that it's correctly output to the text file.
(I know data like name, age, etc. should be private, but to keep this simple I just had everything be public)
class A
{
public:
A() : name(""), age(0) {};
void Save(void)
{
std::string filename = "A.txt";
std::string data;
data += name + "\n";
data += std::to_string(age) + "\n";
std::ofstream outfile(filename);
outfile.write(data.c_str(), data.size());
outfile.close();
}
std::string name;
int age;
};
class B
{
public:
B() : ID(0), points(0) {};
void Save(void)
{
std::string filename = "B.txt";
std::string data;
data += std::to_string(ID) + "\n";
data += std::to_string(points) + "\n";
std::ofstream outfile(filename);
outfile.write(data.c_str(), data.size());
outfile.close();
}
int ID;
int points;
};
int main(void)
{
A a;
B b;
a.name = "Bob"; a.age = 20;
b.ID = 4; b.points = 95;
a.Save();
b.Save();
return 0;
}
A possible solution could be to use metaprogramming (not sure what you mean by metadata), i.e. templates to reuse the common parts
template<typename T1, typename T2>
void TSave(const std::string fname, const T1& p1, const T2& p2) {
std::string filename = fname;
std::stringstream data;
data << p1 << "\n";
data << p2 << "\n";
std::ofstream outfile(filename);
outfile.write(data.str().c_str(), data.str().size());
outfile.close();
}
class A {
...
void Save(void) {
TSave("A.txt", name, age);
}
std::string name;
int age;
};
class B {
...
void Save(void) {
TSave("B.txt", ID, points);
}
int ID;
int points;
};
Live Example
What you are looking for is serialization: saving objects to a file (and one day or another, restore the objects).
Of course, you could write your own serialization framework, and Marco's answer is an interesting start in that direction. But alternatively, you could consider existing libraries, such as boost::serialization :
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
class A {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & name;
ar & age;
}
...
};
class B {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & ID;
ar & points;
}
...
};
main() {
A a;
B b;
...
{
std::ofstream ofs("myfile");
boost::archive::text_oarchive arch(ofs);
arch << a << b;
}
}
As you see, it's still needed to say what's to be written to the file. However, the code is simplified : you don't have to worry about file management and transformation of data. And it works also with standard containers.
You won't find a C++ trick that automatically determines for a class what's to be saved. Two reasons for that:
C++ allows metaprogramming, but it is not reflexive: there are no standard process to find out at execution time which members compose a class.
In an object, some data can be transient, i.e. it means only something at the time of the execution and depends on the context. For example pointers: you could save the value of a pointer to a file, but it will mean nothing when you reload it later (the pointer is only valid until you free the object). The proper way would be to save the object that is pointed to (but where, when, how?).
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 vectors of shared_ptrs. 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.
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.
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;
}
This has been resolved on the development branch.
See. https://svn.boost.org/trac/boost/ticket/9601
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.
I have some problems with boost serialization when serializing derived class through base class pointer. I need a system which serializes some objects as they are being received in the system, so I need to serialize over time. This is not really a problem since I can open a boost::archive::binary_oarchive and serialize objects when required. Rapidly I noticed that boost was performing object tracking by memory address, so the first problem was that different objects in time that share the same memory address were saved as the same object. This can be fixed by using the following macro in the required derived class:
BOOST_CLASS_TRACKING(className, boost::serialization::track_never)
This works fine, but again, when the base class is not abstract, the base class is not serialized properly. In the following example, the base class serialization method is only called once with the first object. In the following, boost assumes that this object has been serialized before although the object has different type.
#include <iostream>
#include <fstream>
#include <boost/serialization/export.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/archive/archive_exception.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
using namespace std;
class AClass{
public:
AClass(){}
virtual ~AClass(){}
private:
double a;
double b;
//virtual void virtualMethod() = 0;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & a;
ar & b;
cout << "A" << endl;
}
};
//BOOST_SERIALIZATION_ASSUME_ABSTRACT(Aclass)
//BOOST_CLASS_TRACKING(AClass, boost::serialization::track_never)
class BClass : public AClass{
public:
BClass(){}
virtual ~BClass(){}
private:
double c;
double d;
virtual void virtualMethod(){};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & boost::serialization::base_object<AClass>(*this);
ar & c;
ar & d;
cout << "B" << endl;
}
};
// define export to be able to serialize through base class pointer
BOOST_CLASS_EXPORT(BClass)
BOOST_CLASS_TRACKING(BClass, boost::serialization::track_never)
class CClass : public AClass{
public:
CClass(){}
virtual ~CClass(){}
private:
double c;
double d;
virtual void virtualMethod(){};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & boost::serialization::base_object<AClass>(*this);
ar & c;
ar & d;
cout << "C" << endl;
}
};
// define export to be able to serialize through base class pointer
BOOST_CLASS_EXPORT(CClass)
BOOST_CLASS_TRACKING(CClass, boost::serialization::track_never)
int main() {
cout << "Serializing...." << endl;
{
ofstream ofs("serialization.dat");
boost::archive::binary_oarchive oa(ofs);
for(int i=0;i<5;i++)
{
AClass* baseClassPointer = new BClass();
// serialize object through base pointer
oa << baseClassPointer;
// free the pointer so next allocation can reuse memory address
delete baseClassPointer;
}
for(int i=0;i<5;i++)
{
AClass* baseClassPointer = new CClass();
// serialize object through base pointer
oa << baseClassPointer;
// free the pointer so next allocation can reuse memory address
delete baseClassPointer;
}
}
getchar();
cout << "Deserializing..." << endl;
{
ifstream ifs("serialization.dat");
boost::archive::binary_iarchive ia(ifs);
try{
while(true){
AClass* a;
ia >> a;
delete a;
}
}catch(boost::archive::archive_exception const& e)
{
}
}
return 0;
}
When executing this piece of code, the result is as follow:
Serializing....
A
B
B
B
B
B
C
C
C
C
C
Deserializing...
A
B
B
B
B
B
C
C
C
C
C
So the base class is only being serialized once, although the derived class has explicitly the track_never flag. There are two different workarounds to fix this behaviour. The first one is to make the base class abstract with a pure virtual method and calling the macro BOOST_SERIALIZATION_ASSUME_ABSTRACT(Aclass), and the second one is to put the track_never flag also in the base class (commented in code).
None of these solutions meets my requirements, since I want to do in the future punctual serializations of the system state, which would require tracking features for a given DClass extending A (not B or C), and also the AClass should not be abstract.
Any hints? Is there any way to call explicitly the base class serialization method avoiding the tracking feature in the base class (that already has been disabled in the derived class)?
After having a little closer look to boost::serialization I'm also convinced there is no straightforward solution for you request.
As you already mentioned the tracking behavior for the serialization is declared on a class by class base with BOOST_CLASS_TRACKING.
This const global information is than interpret in the virtual method tracking from class oserializer.
virtual bool tracking(const unsigned int /* flags */)
Because this is a template class you can explicitly instantiate this method for your classes.
namespace boost {
namespace archive {
namespace detail {
template<>
virtual bool oserializer<class binary_oarchive, class AClass >::tracking(const unsigned int f /* flags */) const {
return do_your_own_tracking_decision();
}
}}}
Now you can try to e.g have something like a global variable and change the tracking behavior from time to time. (E.g depending on which derivate class is written to the archive.)
This seems to wok for “Serializing“ but the “Deserializing“ than throw an exception.
The reason for this is, that the state of “tracking” for each class is only written ones to the archive. Therefore the deserialize does always expect the data for AClass if BClass or CClass is read (at leased if the first write attempt for AClass was with tracking disabled).
One possible solution could be to use the flags parameter in tracking() method.
This parameter represent the flags the archive is created with, default “0”.
binary_oarchive(std::ostream & os, unsigned int flags = 0)
The archive flags are declared in basic_archive.hpp
enum archive_flags {
no_header = 1, // suppress archive header info
no_codecvt = 2, // suppress alteration of codecvt facet
no_xml_tag_checking = 4, // suppress checking of xml tags
no_tracking = 8, // suppress ALL tracking
flags_last = 8
};
no_tracking seems currently not to be supported, but you can now add this behavior to tracking.
template<>
virtual bool oserializer<class binary_oarchive, class AClass >::tracking(const unsigned int f /* flags */) const {
return !(f & no_tracking);
}
Now you can at leased decide for different archives whether AClass should be tracked or not.
boost::archive::binary_oarchive oa_nt(ofs, boost::archive::archive_flags::no_tracking);
And this are the changes in your example.
int main() {
cout << "Serializing...." << endl;
{
ofstream ofs("serialization1.dat");
boost::archive::binary_oarchive oa_nt(ofs, boost::archive::archive_flags::no_tracking);
//boost::archive::binary_oarchive oa(ofs);
for(int i=0;i<5;i++)
{
AClass* baseClassPointer = new BClass();
// serialize object through base pointer
oa_nt << baseClassPointer;
// free the pointer so next allocation can reuse memory address
delete baseClassPointer;
}
ofstream ofs2("serialization2.dat");
boost::archive::binary_oarchive oa(ofs2);
//boost::archive::binary_oarchive oa(ofs);
for(int i=0;i<5;i++)
{
AClass* baseClassPointer = new CClass();
// serialize object through base pointer
oa << baseClassPointer;
// free the pointer so next allocation can reuse memory address
delete baseClassPointer;
}
}
getchar();
cout << "Deserializing..." << endl;
{
ifstream ifs("serialization1.dat");
boost::archive::binary_iarchive ia(ifs);
try{
while(true){
AClass* a;
ia >> a;
delete a;
}
}catch(boost::archive::archive_exception const& e)
{
}
ifstream ifs2("serialization2.dat");
boost::archive::binary_iarchive ia2(ifs2);
try{
while(true){
AClass* a;
ia2 >> a;
delete a;
}
}catch(boost::archive::archive_exception const& e)
{
}
}
return 0;
}
namespace boost {
namespace archive {
namespace detail {
template<>
virtual bool oserializer<class binary_oarchive, class AClass >::tracking(const unsigned int f /* flags */) const {
return !(f & no_tracking);
}
}}}
This still may not be what you are looking for. There are lot more methods which could be adapted with an own implementation. Or your have to derivate your own archive class.
Ultimately the problem seems to be that a boost::serialization archive represents state at a single point in time, and you want your archive to contain state that has changed, i.e. pointers that have been reused. I don't think there is a simple boost::serialization flag that induces the behavior you want.
However, I think there are other workarounds that might be sufficient. You can encapsulate the serialization for a class into its own archive, and then archive the encapsulation. That is, you can implement the serialization for B like this (note that you have to split serialize() into save() and load()):
// #include <boost/serialization/split_member.hpp>
// #include <boost/serialization/string.hpp>
// Replace serialize() member function with this.
template<class Archive>
void save(Archive& ar, const unsigned int version) const {
// Serialize instance to a string (or other container).
// std::stringstream used here for simplicity. You can avoid
// some buffer copying with alternative stream classes that
// directly access an external container or iterator range.
std::ostringstream os;
boost::archive::binary_oarchive oa(os);
oa << boost::serialization::base_object<AClass>(*this);
oa << c;
oa << d;
// Archive string to top level.
const std::string s = os.str();
ar & s;
cout << "B" << endl;
}
template<class Archive>
void load(Archive& ar, const unsigned int version) {
// Unarchive string from top level.
std::string s;
ar & s;
// Deserialize instance from string.
std::istringstream is(s);
boost::archive::binary_iarchive ia(is);
ia >> boost::serialization::base_object<AClass>(*this);
ia >> c;
ia >> d;
cout << "B" << endl;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
Because each instance of B is serialized into its own archive, A is effectively not tracked because there is only one reference per archive of B. This produces:
Serializing....
A
B
A
B
A
B
A
B
A
B
A
C
C
C
C
C
Deserializing...
A
B
A
B
A
B
A
B
A
B
A
C
C
C
C
C
A potential objection to this technique is the storage overhead of encapsulation. The result of the original test program are 319 bytes while the modified test program produces 664 bytes. However, if gzip is applied to both output files then the sizes are 113 bytes for the original and 116 bytes for the modification. If space is a concern then I would recommend adding compression to the outer serialization, which can be easily done with boost::iostreams.
Another possible workaround is to extend the life of instances to the lifespan of the archive so pointers are not reused. You could do this by associating a container of shared_ptr instances to your archive, or by allocating instances from a memory pool.