There are abstract I1 and derived C1.
There are abstract I2 and derived C2.
I1 have shared_ptr<I2>. How can I make them serializable using boost serializaton? I am attemtping do it, but my application get exception.
#include <sstream>
#include <boost/shared_ptr.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
struct I1
{
I1() {}
virtual ~I1() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
struct C1 : I1
{
virtual ~C1() {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I1>(*this);
}
};
struct I2
{
virtual ~I2() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
boost::shared_ptr<I1> p;
};
struct C2 : I2
{
C2() { p = boost::shared_ptr<I1>(new C1); }
virtual ~C2() { }
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I2>(*this);
}
};
int main()
{
C2 c2;
std::string s;
std::stringstream ss(s);
boost::archive::binary_oarchive oa(ss);
oa.register_type<I1>();
oa.register_type<C1>();
oa.register_type<I2>();
oa.register_type<C2>();
oa << c2;
boost::archive::binary_iarchive ia(ss);
//ia.register_type<I1>(); // cannot instantiate abstract class
ia.register_type<C1>();
//ia.register_type<I2>(); // cannot instantiate abstract class
ia.register_type<C2>();
ia >> c2;
}
The boost serialization documentation says here interesting things about BOOST_CLASS_EXPORT:
... BOOST_CLASS_EXPORT ...
Hence, the need for export is implied by the usage of a derived class that is manipulated via a pointer or reference to its base class.
Your p pointer does exactly that. Adding these macros to your code also gets rid of the ugly explicit register_type() calls from your main, which is also nice :)
So, this code seems to compile and work in VS2014:
#include <sstream>
#include <boost/shared_ptr.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/export.hpp>
struct I1
{
I1() {}
virtual ~I1() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
BOOST_CLASS_EXPORT(I1)
struct C1 : I1
{
virtual ~C1() {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I1>(*this);
}
};
BOOST_CLASS_EXPORT(C1)
struct I2
{
virtual ~I2() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
boost::shared_ptr<I1> p;
};
BOOST_CLASS_EXPORT(I2)
struct C2 : I2
{
C2() { p = boost::shared_ptr<I1>(new C1); }
virtual ~C2() { }
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I2>(*this);
}
};
BOOST_CLASS_EXPORT(C2)
int main()
{
C2 c2;
std::string s;
std::stringstream ss(s);
boost::archive::binary_oarchive oa(ss);
oa << c2;
boost::archive::binary_iarchive ia(ss);
ia >> c2;
}
It is interesting, though, that the statement from Boost docs is obviously not true for all compilers, and lots of code examples on the internets just don't work in VS2014.
I think you aren't supposed to register your pure virtual classes on the output archive either.
Alternatively, you can use export. After defining your classes, replace the rest of your code with this:
#include <boost/serialization/export.hpp>
BOOST_CLASS_EXPORT(C1)
BOOST_CLASS_EXPORT(C2)
int main()
{
C2 c2;
std::string s;
std::stringstream ss(s);
{
boost::archive::binary_oarchive oa(ss);
oa << c2;
}
boost::archive::binary_iarchive ia(ss);
ia >> c2;
}
I put the output archive into a separate block. I think it most likely works without it but I want to be sure that everything is flushed (via going out of scope).
Add
BOOST_SERIALIZATION_ASSUME_ABSTRACT(I1)
BOOST_SERIALIZATION_ASSUME_ABSTRACT(I2)
As per the documentation http://www.boost.org/doc/libs/1_39_0/libs/serialization/doc/traits.html#abstract
UPDATE
I've just verified with VS2013RTM and Boost 1_55, it JustWorks(TM), I've
removed the type registrations for abstract bases (they can never be concretely loaded from the archive anyways)
added
#pragma warning(disable: 4244)
#include <boost/config/warning_disable.hpp>
at the top of the file to silence known chatty warnings
Code compiled and runs without error. For good style, you should probably
serialize c2 through a pointer (stack variables can subtly derail object tracking)
Here's the full code I ended up with:
#pragma warning(disable: 4244)
#include <boost/config/warning_disable.hpp>
#include <sstream>
#include <boost/shared_ptr.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
struct I1
{
I1() {}
virtual ~I1() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
struct C1 : I1
{
virtual ~C1() {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I1>(*this);
}
};
struct I2
{
virtual ~I2() = 0 {}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & p;
}
boost::shared_ptr<I1> p;
};
struct C2 : I2
{
C2() { p = boost::shared_ptr<I1>(new C1); }
virtual ~C2() { }
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<I2>(*this);
}
};
int main()
{
boost::shared_ptr<I2> c2(new C2);
std::string s;
std::stringstream ss(s);
boost::archive::text_oarchive oa(ss);
oa.register_type<C1>();
oa.register_type<C2>();
oa << c2;
std::cout << "Serialized form: '" << ss.str() << "'\n";
boost::archive::text_iarchive ia(ss);
ia.register_type<C1>();
ia.register_type<C2>();
ia >> c2;
}
And here's the output:
Related
The following code compiles and seems to serialize properly (that is, the static is saved only once apparently). However, it has an 'input stream error' exception on restore:
#include <boost/serialization/tracking.hpp>
#include <boost/serialization/level.hpp>
#include <boost/serialization/array.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <iostream>
#include <fstream>
#include <array>
class SA {
std::array<char, 1024*1024> sbuf;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int ver) {
ar & boost::serialization::make_array(sbuf.data(), sbuf.size());
};
};
BOOST_CLASS_IMPLEMENTATION(SA, boost::serialization::object_serializable); // serialization_level
BOOST_CLASS_TRACKING(SA, boost::serialization::track_always); // tracking_level
class Foo {
char buf[1024];
inline static SA sxbuf;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int ver) {
ar & boost::serialization::make_array(buf, sizeof(buf));
ar & sxbuf;
};
};
class FooList {
std::array<Foo, 100> fool;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int ver) {
ar & boost::serialization::make_array(fool.data(), fool.size());
};
};
int
main()
{
const std::string filename{"foo.sav"};
FooList x;
std::ofstream out{filename, std::ios::binary};
boost::archive::binary_oarchive oa(out);
oa << x;
std::cout << "Saved\n";
std::ifstream ifs{filename};
boost::archive::binary_iarchive ia(ifs);
if (ifs.fail()) {
std::cerr << "couldn't open input file " << filename << "\n";
return 1;
}
ia >> x; // gives exception
std::cout << "Restored\n";
return 0;
}
The output is as follows:
Saved
terminate called after throwing an instance of 'boost::archive::archive_exception'
what(): input stream error
(I tried this on godbolt too, but -lboost_serialization didn't seem to work, so it wouldn't link properly... possibly my error there).
Any insight much appreciated...
This opens an archive before the writing end is finalized:
std::ofstream out{filename, std::ios::binary};
boost::archive::text_oarchive oa(out);
oa << x;
std::cout << "Saved\n";
std::ifstream ifs{filename};
Also ifs is missing the ios::binary flag.
Other than that, you can
drop make_array,
prefer std::array over T[] and
default the serialization level.
Live On Coliru
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/array.hpp>
#include <array>
#include <fstream>
#include <iostream>
class SA {
std::array<char, 1024 * 1024> sbuf;
friend class boost::serialization::access;
template <class Ar> void serialize(Ar &ar, unsigned) { ar &sbuf; }
};
BOOST_CLASS_TRACKING(SA, boost::serialization::track_always) // tracking_level
class Foo {
std::array<char, 1024> buf;
inline static SA sxbuf;
friend class boost::serialization::access;
template <class Ar> void serialize(Ar &ar, unsigned) { ar &buf &sxbuf; }
};
class FooList {
std::array<Foo, 100> fool;
friend class boost::serialization::access;
template <class Ar> void serialize(Ar &ar, unsigned) { ar &fool; }
};
int main() {
const std::string filename{"foo.sav"};
{
std::ofstream out(filename, std::ios::binary);
boost::archive::binary_oarchive oa(out);
FooList x{};
oa << x;
std::cout << "Saved\n";
}
{
std::ifstream ifs(filename, std::ios::binary);
boost::archive::binary_iarchive ia(ifs);
FooList x{};
ia >> x; // gives exception
std::cout << "Restored\n";
}
}
Prints
Saved
Restored
I try to serialize a class, say B (in file b.h), which is derived from another one, say A (in file a.h). Both classes have private members and I want to serialize both with the boost serialization library non-intrusively. The serialization/deserialization of A does work so far. For the same for the derived class one would use
ar & boost::serialization::base_object<base_class>(*this);
when the intrusive method is used, but where to put it in the non-intrusive case (save/load/serialize or all three?)? And what object has to been used in place of the this pointer?
In the productive code I have derived class a bit more complicated than B. There I got a compiler error which I wasn't able to reproduce in this small example. The compiler message (MSVC 2015, C2665, translated in English):
'boost::serialization::save' : none of the number1 overloads can convert parameter number2 from type 'type'
The Error in German:
Fehler C2665 "boost::serialization::save": Durch keine der 3 Überladungen konnten alle Argumenttypen konvertiert werden. CalorCLI c:\boost_1_61_0\boost\serialization\split_free.hpp 45
Could anyone help?
The Code of a.h :
#pragma once
class A {
private:
int elemA;
public:
A() = default;
A(int elem) : elemA(elem) {};
virtual ~A() = default;
int getElemA() const { return elemA; }
void setElemA(int elem) {
elemA = elem;
}
};
The code of b.h :
#pragma once
#include "a.h"
class B : public A {
private:
int elemB;
public:
B() = default;
B(int elem) : elemB(elem) {};
virtual ~B() = default;
int getElemB() const { return elemB; }
void setElemB(int elem) { elemB = elem; }
};
The Code of the main program:
// TestSerialization.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//
#include <string>
#include <fstream>
#include <iostream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include "b.h"
#include "stdafx.h"
namespace boost {
namespace serialization {
template<class Archive>
void save(Archive & ar, const A & pA, const unsigned int version)
{
ar & pA.getElemA();
}
template<class Archive>
void load(Archive & ar, A & pA, const unsigned int version)
{
int n;
ar & n;
pA.setElemA(n);
}
template<class Archive>
void serialize(Archive & ar, A & pA, const unsigned int version)
{
boost::serialization::split_free(ar, pA, version);
}
template<class Archive>
void save(Archive & ar, const B & pB, const unsigned int version)
{
ar & pB.getElemB();
}
template<class Archive>
void load(Archive & ar, B & pB, const unsigned int version)
{
int n;
ar & n;
pB.setElemB(n);
}
template<class Archive>
void serialize(Archive & ar, B & pB, const unsigned int version)
{
boost::serialization::split_free(ar, pB, version);
}
}
}
int main()
{
A *objA= new A(747);
{
std::ofstream ofs("SavedA");
boost::archive::text_oarchive oa(ofs);
oa << objA;
}
{
A *objA1 = new A();
std::ifstream ifs("SavedA");
boost::archive::text_iarchive ia(ifs);
ia >> objA1;
}
B *objB = new B(747);
{
std::ofstream ofs("SavedB");
boost::archive::text_oarchive oa(ofs);
oa << objB;
}
{
B *objB1 = new B();
std::ifstream ifs("SavedB");
boost::archive::text_iarchive ia(ifs);
ia >> objB1;
}
return 0;
}
First, a fair warning about Quasi-Classes (PDF). They are the enemy of encapsulation and confuse OOP.
Next, let me answer two of your questions real quick and proceed to show my take on this:
Q. where to put it in the non-intrusive case (save/load/serialize or all three?)?
Either in serialize OR in both save and load (if you have split implementations)
Q. what object has to been used in place of the this pointer?
The same object. If you do member-function serialize this points to the same object as gets passed the free function as the second argument. Just use that object.
My Take
Now, let me refer to my answer to Get private data members for non intrusive boost serialization C++
Here's a demonstration of the idea Tanner suggested in his comment
Live On WandBox
a.h
#pragma once
class A {
private:
int elemA;
public:
A(int elem = 0) : elemA(elem) {};
virtual ~A() = default;
int getElemA() const { return elemA; }
void setElemA(int elem) { elemA = elem; }
};
b.h
#pragma once
#include "a.h"
class B : public A {
private:
int elemB;
public:
B(int elem = 0) : A(42), elemB(elem) {};
int getElemB() const { return elemB; }
void setElemB(int elem) { elemB = elem; }
};
main.cpp
#include <string>
#include <sstream>
#include <iostream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include "b.h"
BOOST_CLASS_EXPORT(A)
BOOST_CLASS_EXPORT(B)
namespace privates {
template <typename Key, typename Key::type PointerToMember> struct store {
friend typename Key::type get(Key) { return PointerToMember; }
};
struct elemA {
typedef int A::*type;
friend type get(elemA); // ADL-enable
};
struct elemB {
typedef int B::*type;
friend type get(elemB); // ADL-enable
};
template struct store<elemA, &A::elemA>;
template struct store<elemB, &B::elemB>;
} // namespace privates
auto& getElemA(A& instance) { return instance.*(get(privates::elemA())); }
auto& getElemB(B& instance) { return instance.*(get(privates::elemB())); }
namespace boost {
namespace serialization {
template<class Archive>
void serialize(Archive & ar, A& v, unsigned) { ar & getElemA(v); }
template<class Archive>
void serialize(Archive & ar, B& v, unsigned) { ar & base_object<A>(v) & getElemB(v); }
}
}
template <typename T> void run_tests() {
std::stringstream ss;
{
A *obj= new T(747);
boost::archive::text_oarchive oa(ss);
oa << obj;
delete obj;
}
std::cout << ss.str() << "\n";
{
A *obj = nullptr;
boost::archive::text_iarchive ia(ss);
ia >> obj;
delete obj;
}
}
int main()
{
run_tests<A>();
run_tests<B>();
}
Note it simplifies a few things and at least removed memory-leaks when there were no exceptions.
Output Live On WandBox
22 serialization::archive 15 0 1 0
0 747
22 serialization::archive 15 1 1 B 1 0
0 1 0
1 42 747
Now I got it: Non-intrusive serialization (text format) with a pimpl style struct as described paragraph 3 here let most members private and reduced the overhead of get/set methods. xml is still open - got compiler errors C2664 and C2789 on Visual Studio 2015. Also json could be interesting ...
I am trying to serialize a class member. The following snippet of code will show the relevant class definitions and the non-intrusive serialization code I have included. I am getting a segfault during my saveHashTable() method which tries to serialize the class member shash_table_. Here is a self-contained trimmed version of the code:
#include <map>
#include <string>
#include <vector>
#include <set>
#include <fstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/set.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/list.hpp>
using namespace std;
struct ORROctree
{
public:
struct Node
{
public:
struct Data
{
public:
float n_[3], p_[3];
int id_x_, id_y_, id_z_, lin_id_, num_points_;
std::set<Node*> neighbors_;
void *user_data_;
};
public:
Node::Data *data_;
float center_[3], bounds_[6], radius_;
Node *parent_, *children_;
};
protected:
float voxel_size_, bounds_[6];
int tree_levels_;
Node* root_;
std::vector<Node*> full_leaves_;
};
struct ModelLibrary
{
template <typename T, typename REAL = float>
struct NDIMVoxelStructure
{
T *voxels_;
std::vector<int> total_num_of_voxels_i_;
std::vector<int> num_of_voxels_;
long total_num_of_voxels_;
std::vector<REAL> bounds_;
std::vector<REAL> spacing_;
std::vector<REAL> min_center_;
};
typedef std::pair<const ORROctree::Node::Data*, const ORROctree::Node::Data*> Dipole;
struct Base {
Dipole seg1;
Dipole seg2;
};
typedef std::list<Base> bases_list;
typedef std::map <string, bases_list> SerializeHashTableCell;
// MEMBER TO BE SERIALIZED
typedef NDIMVoxelStructure<SerializeHashTableCell> SerializeHashTable;
public:
SerializeHashTable shash_table_;
public:
bool saveHashTable();
bool loadHashTable();
};
// SERIALIZATION METHODS FOR THE TYPES USED TO FORM THE SERIALIZEHASHTABLE
namespace boost {
namespace serialization {
template<class Archive>
inline void serialize(Archive & ar, ModelLibrary::SerializeHashTable & h, const unsigned int version)
{
ar & h.total_num_of_voxels_;
ar & boost::serialization::make_array(h.voxels_, h.total_num_of_voxels_);
ar & h.num_of_voxels_;
ar & h.total_num_of_voxels_i_;
ar & h.bounds_;
ar & h.spacing_;
ar & h.min_center_;
}
template<class Archive>
inline void serialize(Archive & ar, ModelLibrary::Base & b, const unsigned int version)
{
ar & b.seg1;
ar & b.seg2;
}
template<class Archive>
inline void serialize(Archive & ar, ORROctree::Node n, const unsigned int version)
{
ar & n.data_;
ar & n.center_;
ar & n.bounds_;
ar & n.radius_;
ar & n.parent_;
ar & n.children_;
}
template<class Archive>
inline void serialize(Archive & ar, ORROctree::Node::Data d, const unsigned int version)
{
ar & d.id_x_;
ar & d.id_y_;
ar & d.id_z_;
ar & d.neighbors_;
ar & d.lin_id_;
ar & d.num_points_;
ar & d.p_;
}
}
}
bool ModelLibrary::saveHashTable ()
{
std::ofstream ofs("test.txt");
boost::archive::text_oarchive oa(ofs);
oa << shash_table_;
return true;
}
bool
ModelLibrary::loadHashTable ()
{
std::ifstream ifs("test.txt");
boost::archive::text_iarchive ia(ifs);
ia >> shash_table_;
return true;
}
int main()
{
ModelLibrary m;
m.saveHashTable();
}
You just need to initialize the data in your data structures. All primitive types that haven't been explicitely initialized will have indeterminate ("random") values. This includes the pointers, which means you're invoking Undefined Behaviour by dereferencing pointers to random memory locations.
Here's an update with the simplest initialization that could possibly work, and it runs clean under Valgrind. That's a good tool. Use it!
Also, crank up the compiler messages (-Wall -Wextra -pedantic at a minimum for gcc/clang).
Added in various places:
Data() : id_x_(0), id_y_(0), id_z_(0), lin_id_(0), num_points_(0), user_data_(0)
{
std::fill(n_, n_+3, 0);
std::fill(p_, p_+3, 0);
}
Node() : data_(0), radius_(0), parent_(0), children_(0)
{
std::fill(center_, center_+3, 0);
std::fill(bounds_, bounds_+6, 0);
}
ORROctree() : voxel_size_(0), tree_levels_(0), root_(0)
{
std::fill(bounds_, bounds_+6, 0);
}
NDIMVoxelStructure() : voxels_(0), total_num_of_voxels_(0)
{ }
Base() : seg1(0, 0), seg2(0, 0)
{ }
See it Live On Coliru
Update From the comment: the following lines were clearly in error:
inline void serialize(Archive & ar, ORROctree::Node n, const unsigned int version)
inline void serialize(Archive & ar, ORROctree::Node::Data d, const unsigned int version)
And should have read
inline void serialize(Archive & ar, ORROctree::Node& n, const unsigned int version)
inline void serialize(Archive & ar, ORROctree::Node::Data& d, const unsigned int version)
When trying to serialize a derived class with boost::serialization, an assertion in boost/archive/detail/oserializer.hpp (line 436 in Boost 1.53.0) is tripped:
const basic_pointer_oserializer * bpos
= static_cast<const basic_pointer_oserializer *>(
boost::serialization::singleton<
archive_serializer_map<Archive>
>::get_const_instance().find(*true_type)
);
BOOST_ASSERT(NULL != bpos);
Here is my MWE:
#include <iostream>
#include <fstream>
#include <boost/archive/polymorphic_xml_oarchive.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
class base
{
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::make_nvp("test", test);
}
int test;
public:
virtual void print() { std::cout << 1 << std::endl; }
};
class derived : public base
{
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::make_nvp("base", boost::serialization::base_object<base>(*this));
ar & boost::serialization::make_nvp("test2", test2);
}
int test2;
public:
void print() { std::cout << 2 << std::endl; }
};
int main (int argc, char const *argv[])
{
base *b = new derived();
std::ofstream stream("out.xml");
boost::archive::polymorphic_xml_oarchive ar(stream);
ar.register_type<derived>();
ar << boost::serialization::make_nvp("b", b);
return 0;
}
I also tried using BOOST_CLASS_EXPORT_GUID instead of register_type, but that also didn't work. Any idea what I'm doing wrong?
You probably don't mean to use polymorphic_xml_archive (see docs).
Notably, polymorphic archives have to do with the interface of the archives, nothing with the entities you're serializing for (they can always be polymorphic).
If you use xml_archive instead, the problem vanishes.
In my application, there are agents of different types. I am planning to use boost serialization for sending/receiving data between agents.(by sending/receiving, I actually mean writing/reading operation the serialization target file)
A receiving agent may receive data of different data whose type is not known in advance. Suppose the data format has a general structure like this:
class Base
{
public:
int message_type_id;
}
class Derived_1
{
public:
Derived_1(int message_type_id_):message_type_id(message_type_id_){}
struct data_1 {...};
};
class Derived_2
{
public:
Derived_2(int message_type_id_):message_type_id(message_type_id_){}
struct data_2 {...};
};
the sending agent can send(i.e serialize) any of the two derived types. Similarly, the receiving agent can receive(i.e de-serialize) any of the two derived types; while what I can see in the tutorial(Dumping derived classes through base class pointers) is like this:
void save()
{
std::ofstream file("archive.xml"); //target file
boost::archive::xml_oarchive oa(file);
oa.register_type<date>( );// you know what you are sending, so you make proper modifications here to do proper registration
base* b = new date(15, 8, 1947);
oa & BOOST_SERIALIZATION_NVP(b);
}
void load()
{
std::ifstream file("archive.xml"); //target file
boost::archive::xml_iarchive ia(file);
ia.register_type<date>( );// I don't know which derived class I am receiving, so I can't do a proper registration
base *dr;
ia >> BOOST_SERIALIZATION_NVP(dr);
date* dr2 = dynamic_cast<date*> (dr);
std::cout << dr2;
}
as you can see, xml_oarchive and xml_iarchive do a register_type<date> before serialize/deserialization. so the receiving end will know in advance what to dynamic_cast to.
whereas in my case, since I know what I am sending, I can do proper registration&serialization on case-to-case basis. However, on the receiving end, I dont't know in advance what to register and what to dynamic cast.
Is there a way I can tell the type in advance so that the receiving can do a casting?
thanks
EDIT:
Here is the simplified modification of demo.cpp I save an object, and then restore it.
#include <cstddef> // NULL
#include <iomanip>
#include <iostream>
#include <fstream>
#include <string>
#include <boost/archive/tmpdir.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/assume_abstract.hpp>
/*
bus_stop is the base class.
bus_stop_corner and bus_stop_destination are derived classes from the above base class.
bus_route has a container that stores pointer to the above derived classes
*/
class bus_stop
{
friend class boost::serialization::access;
virtual std::string description() const = 0;
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & type;
}
protected:
public:
std::string type;
bus_stop(){type = "Base";}
virtual ~bus_stop(){}
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(bus_stop)
class bus_stop_corner : public bus_stop
{
friend class boost::serialization::access;
virtual std::string description() const
{
return street1 + " and " + street2;
}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
// save/load base class information
ar & boost::serialization::base_object<bus_stop>(*this);
ar & street1 & street2;
}
public:
std::string street1;
std::string street2;
bus_stop_corner(){}
bus_stop_corner(
const std::string & _s1, const std::string & _s2
) :
street1(_s1), street2(_s2)
{
type = "derived_bs_corner";
}
};
class bus_stop_destination : public bus_stop
{
friend class boost::serialization::access;
virtual std::string description() const
{
return name;
}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<bus_stop>(*this) & name;
}
public:
std::string name;
bus_stop_destination(){}
bus_stop_destination(
const std::string & _name
) :
name(_name)
{
type = "derived_bs_destination";
}
};
class bus_route
{
friend class boost::serialization::access;
typedef bus_stop * bus_stop_pointer;
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar.register_type(static_cast<bus_stop_corner *>(NULL));
ar.register_type(static_cast<bus_stop_destination *>(NULL));
ar & stops;
}
public:
std::list<bus_stop_pointer> stops;
bus_route(){}
void append(bus_stop *_bs)
{
stops.insert(stops.end(), _bs);
}
};
//BOOST_CLASS_VERSION(bus_route, 2)
void save_schedule(const bus_route s, const char * filename){
// make an archive
std::ofstream ofs(filename);
boost::archive::text_oarchive oa(ofs);
oa << s;
}
void
restore_schedule(bus_route &s, const char * filename)
{
// open the archive
std::ifstream ifs(filename);
boost::archive::text_iarchive ia(ifs);
// restore the schedule from the archive
ia >> s;
}
int main(int argc, char *argv[])
{
bus_stop *bs1 = new bus_stop_corner(
"First St", "Second st"
);
bus_stop *bs2 = new bus_stop_destination(
"myName"
);
// make a routes
bus_route original_route;
original_route.append(bs1);
original_route.append(bs2);
std::string filename1(boost::archive::tmpdir());
filename1 += "/demofile1.txt";
save_schedule(original_route, filename1.c_str());
bus_route new_route ;
restore_schedule(new_route, filename1.c_str());
////////////////////////////////////////////////////////
std::string filename2(boost::archive::tmpdir());
filename2 += "/demofile2.txt";
save_schedule(new_route, filename2.c_str());
delete bs1;
delete bs2;
return 0;
}
The old and new objects are not equal coz again saving(serializing) the new object to another file results in a different(empty) content. Can you please let me know how I can fix this code to deserialize the derived classes successfully? many thanks
EDIT-2
There is nothing wrong with the above code now(after a small typo was fixed).
I am answering my own question here coz there is another good approach suggested by someone else.
So the Answer to my first question is like this :
As long as you register the derived types in the main serialization function (in the above case: serialize() in bus_route class) everything should be fine.
thanks for all the help
A solution is to (de-)serialize boost::shared_ptr<Base>. The following code demonstrates it. After deserialization the pDst is an instance of the Derived_1 class. The code complied using an online compiler is available on this link.
#include <boost/serialization/access.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/noncopyable.hpp>
#include <boost/make_shared.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
class Base {
friend class boost::serialization::access;
public:
Base();
virtual ~Base();
private:
template<class Archive> void serialize(Archive &ar, const unsigned int version) {}
public:
virtual bool operator ==(const Base &rh) const = 0;
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Base)
BOOST_SERIALIZATION_SHARED_PTR(Base)
Base::Base() {
}
Base::~Base() {
}
class Derived_1 : boost::noncopyable, public Base {
friend class boost::serialization::access;
public:
int m_iValue;
public:
Derived_1();
Derived_1(int iValue);
private:
template<class Archive> void serialize(Archive &ar, const unsigned int version) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
ar & boost::serialization::make_nvp("value", m_iValue);
}
public:
bool operator ==(const Base &rh) const;
};
BOOST_SERIALIZATION_SHARED_PTR(Derived_1)
Derived_1::Derived_1() : m_iValue(0) {
}
Derived_1::Derived_1(int iValue) : m_iValue(iValue) {
}
bool Derived_1::operator==(const Base &rh) const {
const Derived_1 *pRH = dynamic_cast<const Derived_1 *>(&rh);
return pRH != nullptr && pRH->m_iValue == this->m_iValue;
}
#include <boost/serialization/export.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/make_shared.hpp>
#include <sstream>
#include <string>
BOOST_CLASS_EXPORT_GUID(Base, "base")
BOOST_CLASS_EXPORT_GUID(Derived_1, "derived_1")
void test(void) {
std::string str;
boost::shared_ptr<Base> pSrc = boost::make_shared<Derived_1>(10);
boost::shared_ptr<Base> pDst;
{
std::ostringstream ofs;
boost::archive::xml_oarchive oa(ofs);
oa << boost::serialization::make_nvp("item", pSrc);
str = ofs.str();
}
{
std::istringstream ifs(str);
boost::archive::xml_iarchive ia(ifs);
ia >> boost::serialization::make_nvp("item", pDst);
}
if (*pSrc == *pDst) {
printf("Success\n");
}
else {
printf("Fail\n");
}
}
int main(int argc, char* argv[]) {
test();
}