How do I serialize a class containing pointers to primitives? - c++

I am trying to use boost's functionality for serializing pointers to primitives (so that I don't have to de-reference and do a deep store myself). However, I get a pile of errors when I try to do it. Here is a simple example of a class that is supposed to contain save and load methods which write and read the class content from a file. This program does not compile:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <fstream>
class A
{
public:
boost::shared_ptr<int> sp;
int const * p;
int const& get() {return *p;}
void A::Save(char * const filename);
static A * const Load(char * const filename);
//////////////////////////////////
// Boost Serialization:
//
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar,const unsigned int file_version)
{
ar & p & v;
}
};
// save the world to a file:
void A::Save(char * const filename)
{
// create and open a character archive for output
std::ofstream ofs(filename);
// save data to archive
{
boost::archive::text_oarchive oa(ofs);
// write the pointer to file
oa << this;
}
}
// load world from file
A * const A::Load(char * const filename)
{
A * a;
// create and open an archive for input
std::ifstream ifs(filename);
boost::archive::text_iarchive ia(ifs);
// read class pointer from archive
ia >> a;
return a;
}
int main()
{
}
Note that I am not interested in a solution that dereferences the pointer; I want boost to take care of that for me (many of these classes might be pointing to the same underlying object).

In http://www.boost.org/doc/libs/1_54_0/libs/serialization/doc/index.html:
By default, data types designated primitive by Implementation Level
class serialization trait are never tracked. If it is desired to track
a shared primitive object through a pointer (e.g. a long used as a
reference count), It should be wrapped in a class/struct so that it is
an identifiable type. The alternative of changing the implementation
level of a long would affect all longs serialized in the whole program
- probably not what one would intend.
Hence:
struct Wrapped {
int value;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar,const unsigned int file_version)
{
ar & value;
}
};
boost::shared_ptr<Wrapped> sp;
Wrapped const * p;

Related

Common confusions with serializing polymorphic types

I have seen many questions, tutorials, and documentation involving serializing derived classes, and I haven't been able to reach a consensus on several issues, including (and illustrated in the following code):
boost::serialization::base_object vs BOOST_SERIALIZATION_BASE_OBJECT_NVP
archive & mData; vs archive & BOOST_SERIALIZATION_NVP(mData);
The usefulness of BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);
Requiring serialize() for a class in the hierarchy that doesn't need to serialize anything.
Code:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/base_object.hpp>
#include <fstream>
class AbstractPoint
{
public:
virtual ~AbstractPoint(){}
virtual void DoSomething() = 0;
// Even though the class is abstract, we still need this
template<class TArchive>
void serialize(TArchive& archive, const unsigned int version)
{
// do nothing
}
};
// This doesn't seem to do anything
//BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);
class Point : public AbstractPoint
{
public:
Point() = default;
Point(const double data) : mData(data) {}
void DoSomething(){}
template<class TArchive>
void serialize(TArchive& archive, const unsigned int version)
{
// These two seem equivalent. Without one of them, unregistered void cast
archive & boost::serialization::base_object<AbstractPoint>(*this);
//archive & BOOST_SERIALIZATION_BASE_OBJECT_NVP(AbstractPoint);
// These two seem equivalent
archive & mData;
//archive & BOOST_SERIALIZATION_NVP(mData);
}
double mData;
};
int main()
{
std::shared_ptr<AbstractPoint> point(new Point(7.4));
std::ofstream outputStream("test.txt");
boost::archive::text_oarchive outputArchive(outputStream);
outputArchive.register_type<Point>();
outputArchive << point;
outputStream.close();
std::shared_ptr<AbstractPoint> pointRead;
std::ifstream inputStream("test.txt");
boost::archive::text_iarchive inputArchive(inputStream);
inputArchive.register_type<Point>();
inputArchive >> pointRead;
std::shared_ptr<Point> castedPoint = std::dynamic_pointer_cast<Point>(pointRead);
std::cout << castedPoint->mData << std::endl;
return 0;
}
The other major issue is where to register classes in a "real" environment (when there is linking, etc.), but that seems worth a separate question.
It would be great to have a "gold standard" example of these kinds of things in the documentation, but at the least on StackOverflow :)
boost::serialization::base_object vs BOOST_SERIALIZATION_BASE_OBJECT_NVP
The NVP wrapper is only ever required for archives that have element naming, like XML.
Unless you use it, base_object<> is cleaner and simpler.
archive & mData; vs archive & BOOST_SERIALIZATION_NVP(mData);
Ditto
The usefulness of BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);
I assume it will merely be an optimization - suppressing registered type information with each archive type, since you told the framework it will never be de-serializing instances of the type
Requiring serialize() for a class in the hierarchy that doesn't need to serialize anything.
You don't need it, unless you need the type information about a polymorphic base there. When do you need that? When you need to de-serialize pointers of the base type.
Hence, if you have
struct A{ virtual ~A(); };
struct B:A{};
struct C:B{};
struct D:B{};`
you will need serialization for A (but not B) if you (de)serialize A*. You will need serialization for B if you (de)serialize B*.
Similarly, if your type is not polymorphic (virtual) or you don't use it as such, you don't need any base serialization (e.g. if you (de)serialize C or D directly).
Finally, if you have struct A{}; struct B:A{}; there is no need to tell Boost Serialization about the base type at all, (you could just do the serialization from within B).
Update in response to your samples:
case1.cpp looks ok
case2.cpp needs to call base serialization, of course; not necessarily using base_object because you require polymorphic serialization:
template<class TArchive> void serialize(TArchive& archive, unsigned) {
archive & boost::serialization::base_object<AbstractPoint>(*this)
& mData;
// OR:
archive & static_cast<AbstractPoint&>(*this)
& mData;
// OR even just:
archive & mParentData
& mData;
}
case3.cpp: indeed, it's exactly like case1, but with dynamic allocation and object tracking
case4.cpp: is exactly like case1, but with dynamic allocation and object tracking; NB!! it requires explicitly serializing for the base!
template<class TArchive> void serialize(TArchive& archive, unsigned) {
archive & boost::serialization::base_object<AbstractPoint>(*this)
& mData;
}
case5.cpp: yes, but it's more typical to use the CLASS_EXPORT* macros from boost/serialization/export.hpp
Bitrot insurance:
case1.cpp
case2.cpp
case3.cpp
case4.cpp
case5.cpp
Following #sehe's advice, here are some example uses:
Serialize derived class object, not forwarding to parent
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
class AbstractPoint
{
public:
virtual ~AbstractPoint(){}
virtual void DoSomething() = 0;
};
class Point : public AbstractPoint
{
public:
Point() = default;
Point(const double data) : mData(data) {}
void DoSomething(){}
template<class TArchive>
void serialize(TArchive& archive, const unsigned int version)
{
archive & mData;
}
double mData;
};
int main()
{
Point point(7.4);
std::ofstream outputStream("test.txt");
boost::archive::text_oarchive outputArchive(outputStream);
outputArchive << point;
outputStream.close();
Point pointRead;
std::ifstream inputStream("test.txt");
boost::archive::text_iarchive inputArchive(inputStream);
inputArchive >> pointRead;
std::cout << pointRead.mData << std::endl;
return 0;
}
Serialize derived class object, including (automatic) forwarding to parent:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
class AbstractPoint
{
public:
virtual ~AbstractPoint(){}
virtual void DoSomething() = 0;
double mParentData = 3.1;
template<class TArchive>
void serialize(TArchive& archive, const unsigned int version)
{
archive & mParentData;
}
};
class Point : public AbstractPoint
{
public:
Point() = default;
Point(const double data) : mData(data) {}
void DoSomething(){}
template<class TArchive>
void serialize(TArchive& archive, const unsigned int version)
{
// this is not required, the parent serialize() seems to be called automatically
// archive & boost::serialization::base_object<AbstractPoint>(*this);
archive & mData;
}
double mData;
};
int main()
{
Point point(7.4);
std::ofstream outputStream("test.txt");
boost::archive::text_oarchive outputArchive(outputStream);
outputArchive << point;
outputStream.close();
Point pointRead;
std::ifstream inputStream("test.txt");
boost::archive::text_iarchive inputArchive(inputStream);
inputArchive >> pointRead;
std::cout << pointRead.mParentData << std::endl;
std::cout << pointRead.mData << std::endl;
return 0;
}
Serialize derived class pointer, not forwarding to parent
(note nothing changes from the object case)
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <fstream>
class AbstractPoint
{
public:
virtual ~AbstractPoint(){}
virtual void DoSomething() = 0;
};
class Point : public AbstractPoint
{
public:
Point() = default;
Point(const double data) : mData(data) {}
void DoSomething(){}
template<class TArchive>
void serialize(TArchive& archive, const unsigned int version)
{
archive & mData;
}
double mData;
};
int main()
{
std::shared_ptr<Point> point(new Point(7.4));
std::ofstream outputStream("test.txt");
boost::archive::text_oarchive outputArchive(outputStream);
outputArchive << point;
outputStream.close();
std::shared_ptr<Point> pointRead;
std::ifstream inputStream("test.txt");
boost::archive::text_iarchive inputArchive(inputStream);
inputArchive >> pointRead;
std::cout << pointRead->mData << std::endl;
return 0;
}
Serialize derived class pointer, forwarding to parent
(note nothing changes from the object case)
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <fstream>
class AbstractPoint
{
public:
virtual ~AbstractPoint(){}
virtual void DoSomething() = 0;
template<class TArchive>
void serialize(TArchive& archive, const unsigned int version)
{
archive & mParentData;
}
double mParentData = 3.1;
};
class Point : public AbstractPoint
{
public:
Point() = default;
Point(const double data) : mData(data) {}
void DoSomething(){}
template<class TArchive>
void serialize(TArchive& archive, const unsigned int version)
{
archive & mData;
}
double mData;
};
int main()
{
std::shared_ptr<Point> point(new Point(7.4));
std::ofstream outputStream("test.txt");
boost::archive::text_oarchive outputArchive(outputStream);
outputArchive << point;
outputStream.close();
std::shared_ptr<Point> pointRead;
std::ifstream inputStream("test.txt");
boost::archive::text_iarchive inputArchive(inputStream);
inputArchive >> pointRead;
std::cout << pointRead->mParentData << std::endl;
std::cout << pointRead->mData << std::endl;
return 0;
}
Serialize base class pointer
(We now have to register the type of the derived class with the archives, as well as use boost::serialization::base_object)
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/base_object.hpp>
#include <fstream>
class AbstractPoint
{
public:
virtual ~AbstractPoint(){}
virtual void DoSomething() = 0;
// This is required if we want to serialize an AbstractPoint pointer
template<class TArchive>
void serialize(TArchive& archive, const unsigned int version)
{
// do nothing
}
};
class Point : public AbstractPoint
{
public:
Point() = default;
Point(const double data) : mData(data) {}
void DoSomething(){}
template<class TArchive>
void serialize(TArchive& archive, const unsigned int version)
{
// Without this, we get unregistered void cast
archive & boost::serialization::base_object<AbstractPoint>(*this);
archive & mData;
}
double mData;
};
int main()
{
std::shared_ptr<AbstractPoint> point(new Point(7.4));
std::ofstream outputStream("test.txt");
boost::archive::text_oarchive outputArchive(outputStream);
outputArchive.register_type<Point>();
outputArchive << point;
outputStream.close();
std::shared_ptr<AbstractPoint> pointRead;
std::ifstream inputStream("test.txt");
boost::archive::text_iarchive inputArchive(inputStream);
inputArchive.register_type<Point>();
inputArchive >> pointRead;
std::shared_ptr<Point> castedPoint = std::dynamic_pointer_cast<Point>(pointRead);
std::cout << castedPoint->mData << std::endl;
return 0;
}

Vector of pointers. BOOST serialization

I want to serialize/deserialize using BOOST the values (not the pointers) of objects in the following vector:
std :: vector <A*> m_vector;
To serialize I use the following code:
int nItems = m_vector.size();
ar & nItems;
std::for_each(m_vector.begin(), m_vector.end(), [&ar](A* pItem) {
ar & *pItem;
});
And to deserialize:
int nItems;
ar & nItems;
for (int i = 0; i < nItems; ++i) {
A* pItem;
ar & *pItem; ///////////// Run-Time Check Failure #3
m_vector.push_back(pItem);
}
But when I run the program I get the following error:
Run-Time Check Failure # 3 - The variable 'pItem' is Being Used without Being initialized.
What am I doing wrong?
Thank you.
You will need to allocate memory for the object pointed to by pItem:
A* pItem = new A;
ar & *pItem;
m_vector.push_back(pItem);
The error was because although you had a pointer, there was no object at the memory location where the pointer pointed to -- the value of the pointer was garbage (uninitialized pointer).
Don't forget to call delete when you no longer need the object pointed to by the pointer in the vector to preven memory leak. Better yet, use a smart pointer (e.g. boost::shared_ptr<>) to ensure the memory is deallocated when no longer accessible.
2 years later, but worth mentioning.
There's been a better solution to serialize a vector of pointers to objects or any other STL container (list, set etc.).
In order to serialize a vector, add:
#include <boost/serialization/vector.hpp>
and then you need to implement serialize() method and friend your class with archieve.
Everything is explained in this example (read all comments carefully, they're very important):
#include <fstream>
#include <iostream>
#include <vector>
#include <iostream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
class Abc{
// In order to make Abc serializable
// you need to friend this lass with serialization::access
friend class boost::serialization::access;
// and then add this method
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// choose what class fields do you want to serialize
ar & a;
ar & b;
ar & c;
}
public:
int a;
int b;
int c;
// don't forget about default constructor! It's necessary for serialization!
Abc(){};
Abc(int a, int b, int c): a(a), b(b), c(c){};
};
class GpsPosition
{
private:
// as mentioned above...
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & degrees;
ar & minutes;
ar & seconds;
ar & wektorIntow;
}
int degrees;
int minutes;
float seconds;
public:
std::vector<Abc*> abcVector;
GpsPosition(){};
GpsPosition(int d, int m, float s): degrees(d), minutes(m), seconds(s)
{
// adding some objects to abcVector
abcVector.push_back(new Abc(1, 2, 3));
abcVector.push_back(new Abc(3, 2, 3));
abcVector.push_back(new Abc(2, 2, 3));
abcVector.push_back(new Abc(1, 2, 3));
}
int getDegrees(){ return this->degrees; }
int getMinutes(){ return this->minutes; }
float getSeconds(){ return this->seconds; }
};
int main(){
// And now how to use it
// Saving to file:
std::ofstream fileHandler("filename");
const GpsPosition position1(35, 59, 24.567f);
{
boost::archive::text_oarchive boostOutputArchieve(fileHandler);
boostOutputArchieve << position1;
}
// Reading from file:
GpsPosition newPosition;
{
std::ifstream fileHandler;
try{
fileHandler.open("filenacme");
boost::archive::text_iarchive boostInputArchieve(fileHandler);
// read class state from archive
boostInputArchieve >> newPosition;
// archive and stream closed when destructors are called
fileHandler.close();
}
catch (std::ifstream::failure e) {
std::cerr << "Exception opening/reading/closing file";
}
catch(boost::archive::archive_exception e){
std::cerr << "Exception opening/reading/closing file";
}
}
// print to the console
std::cout << newPosition.getMinutes() << std::endl;
std::cout << newPosition.abcVector[0]->a;
std::cin.get();
return 0;
}
For more information, check out this tutorial:
http://www.boost.org/doc/libs/1_55_0/libs/serialization/doc/index.html

Construct object from boost serialization archive

Is it possible to construct objects from directly from the archive?
Something like this...
// Non-working pseudo code
struct Foo {
BOOST_SERIALIZATION_SPLIT_MEMBER();
std::vector<int> data;
Foo() {
// populate "data" by doing calculation
data.push_back(1); data.push_back(2);
}
template<class Archive>
Foo( Archive & ar ) {
// populate "data" by rading the archive
}
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
// Normal serialization of data
ar << data;
}
};
int main(int argc, const char *argv[])
{
// deserialize
boost::archive::text_iarchive oar(std::cin);
Foo foo(oar);
return 0;
}
You can use a deserializing constructor:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
class Point
{
public:
Point() = default;
Point(boost::archive::text_iarchive& archive)
{
archive >> *this;
}
float x = 1.;
float y = 2.;
private:
friend class boost::serialization::access;
template<class TArchive>
void serialize(TArchive & archive, const unsigned int version)
{
archive & x;
archive & y;
}
};
int main()
{
Point p;
p.x = 5;
p.y = 6;
std::ofstream outputStream("test.archive");
boost::archive::text_oarchive outputArchive(outputStream);
outputArchive << p;
outputStream.close();
std::ifstream inputStream("test.archive");
boost::archive::text_iarchive inputArchive(inputStream);
Point pointRead(inputArchive);
std::cout << pointRead.x << " " << pointRead.y << std::endl;
return 0;
}
As I said in the comment.
Yes there is no problem with constructing from an archive.
(Another alternative is to have static load function but that can have performance penalties).
The only potential problem I see with your approach is that your constructor can take almost anything as an argument and that can create problems.
And that can interfere with the copy constructor and other single argument constructors relying in implicit conversion.
So one has to restrict to take archives only.
There are different methods to do this, but based in this conversation http://marc.info/?l=boost&m=121131260728308&w=2, and by the fact that the inheritance tree of the archives is documented http://www.boost.org/doc/libs/1_35_0/libs/serialization/doc/class_diagram.html, I think this is the best solution is to check that the argument derives from basic_iarchive.
#include<type_traits>
struct Foo {
...
std::vector<int> data;
Foo() {
// populate "data" by doing calculation
data.push_back(1); data.push_back(2);
}
template<class IArchive,
typename = std::enable_if_t<std::is_base_of<boost::archive::detail::basic_iarchive, IArchive>::value>>
Foo( IArchive & ar ) {
ar >> data;
// populate "data" by reading the archive
}
...
};
int main(int argc, const char *argv[])
{
// deserialize
boost::archive::text_iarchive iar(std::cin);
Foo foo(iar); // will also work with other archives
}
As for what happens when your data is not default constructive see the discussion above.

boost:serialization reconstruction (loading)

I'm using boost:serialization to save data structures into a file. The actual data is a pointer vector of classes and subclasses.
However the constructor of the class which is serialized takes as parameter another instantiated class Agent which is an object that controls communication with a simulation API (webots).
I see that in boost::serialization examples, the serializable objects need an empty constructor class() {}; to be used for the reconstruction. However this is impractical in my case. How can I use reconstruction but include the object which communicates with the API ?
One of the serializable classes has this constructor:
State(Agent &A, ACTION_MODE const& m);
and I've seen from examples in boost docs that I need something like this:
State() {};
Yet Agent &A must be passed as parameter.
Should I find a way around this (using an extern, a singleton, a global object) or is there a way to modify this behavior when reconstructing ? I'm sure I'm missing something here.
Thank you
EDIT:
Maybe I didn't explain this clearly enough. I get an error message when trying to "load" by reconstructing the serialized data.
error: no matching function to call State::State()
Which is what made me look into boost::serialize code, and think that it is calling a constructor or copy operator.
How do I make it use a specific constructor to serialize the data and take as an argument the Agent reference &a ?
EDIT#2:
template <class S, class P, class A> void Task<S,P,A>::save(const char* file)
{
std::ofstream ofs(file);
assert(ofs.good());
boost::archive::text_oarchive oa(ofs);
oa << states;
ofs.close();
}
template <class S, class P, class A> void Task<S,P,A>::load(const char* file)
{
std::ifstream ifs(file);
boost::archive::text_iarchive ia(ifs);
ia >> states;
ifs.close();
}
States is friend to boost::serialization::access and has a function serialize.
Saving works fine, loading is the problem.
states is: boost::ptr_vector<S> states; where S is a type of State polymorphic class.
State is the base class and has "serialize"
template <class Archive>
void State::serialize(Archive& ar, const unsigned int version)
{
ar & accel.Xaxis & accel.Yaxis & accel.Zaxis;
ar & gyro.Xaxis & gyro.Yaxis & gyro.Zaxis;
ar & gps.Yaxis;
ar & positions;
ar & reward & value & hash_value;
}
guState inherits from State.
template <class Archive>
void guState::serialize(Archive& ar, const unsigned int version)
{
ar & boost::serialization::base_object<State>(*this);
ar & accel.Xaxis & accel.Yaxis & accel.Zaxis;
ar & gyro.Xaxis & gyro.Yaxis & gyro.Zaxis;
ar & gps.Yaxis;
ar & positions;
ar & reward & value & hash_value;
}
accel, gyro, gps are simple structures with 3 double variables. They get serialized above^^.
Positions is an std::map<std::string,float> positions;
Looking at the serialized text file, everything appears Ok.
I cannot understand why it calls a constructor when trying to load the file.
EDIT#3:
Base Constructor is:
State(Agent &A, ACTION_MODE const& m);
Derived Constuctor is:
guState::guState(Agent& A, ACTION_MODE const& m) :
State(A, m)
{
...
}
Agent reference &A, kept in each State (or derived State) refers to an object obtained from the simulation API. It controls a robot. I cannot serialize it, and it doesn't make sense serializing it.
When I use:
namespace boost { namespace serialization {
template <class Archive>
void save_construct_data(Archive & ar,const guState* d,const unsigned int file_version)
{
ar << guState::caller;
ar << guState::mode;
}
template <class Archive>
void load_construct_data(Archive & ar, guState* d,const unsigned int file_version)
{
Agent &a;
ACTION_MODE &m;
ar >> a;
ar >> m;
::new(d) guState(a,m);
}
}
}
I get the following errors:
invalid use of non-static data member State::caller
invalid use of non-static data member State::mode
referring to the references used from the constructor.
And:
error: 'a' declared as reference but not initialized
error: 'm' declared as reference but not initialized
As you can see, it makes no sense to try and save the reference to Agent, because that reference (even if it could be saved or serialized) will probably be different every time the application is started.
And in loading the construct data, apart from me probably using the wrong syntax, it makes no sense to construct from a serialized reference to agent.
What I belive i need, is a way to tell the load_construct_data how to obtain a reference to an Agent (after initializing an agent object) and use that reference to construct the data.
Does that make any sense ? Do you think this is doable ?
EDIT#4
namespace boost { namespace serialization {
template <class Archive>
void save_construct_data(Archive & ar,const guState* d,const unsigned int file_version)
{
ar << guState::caller;
}
template <class Archive>
void load_construct_data(Archive & ar, guState* d,const unsigned int file_version)
{
Agent * a;
ACTION_MODE mode = RAND_SING;
ar >> a;
::new(d) guState(*a,mode);
}
}
}
It will not allow to serialize guState::caller
I have also made class Agent serializable, and overloaded the load_construct_data and save_construct_data of Agent in order to request from the simulation app a new instance of Agent to control the API.
EDIT:
There is a part of the manual that I think we both missed: The section non-default constructors here. To make it work you need to a save_construct_data and a load_construct_data function. There is a slight technicallity in terms of where these are friended explored here.
Also, you said you had this problem when trying to load only, but that you could save fine. This makes me think that you might have omitted
BOOST_CLASS_EXPORT_GUID(state, "state")
This omission might lead to segmentation faults once you get the load compiling (see the export section of the manual)
To make sure I was not mistaken, I made a compiling example, which I add in case its useful.
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <iostream>
#include <fstream>
//base class
struct base
{
base(double d) : m_d(d) {}
virtual double run() = 0;
private:
friend class boost::serialization::access;
double m_d;
template <class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & m_d;
}
};
//forward declare the save construct data before friending it
// (something about friend being in a different namespace)
class derived;
namespace boost { namespace serialization {
template<class Archive>
inline void save_construct_data(Archive & ar, const derived * t, const unsigned int file_version);
}}
//derived class with non-default constructor
struct derived : public base
{
derived(double a , double b) :
base(a+b),
m_a(a),m_b(b),m_c(a*b)
{}
//some checks
double get_a() const {return m_a;}
double get_b() const {return m_b;}
double get_c() const {return m_c;}
double run(){return 1.0;}
private:
friend class boost::serialization::access;
template<class Archive>
friend void boost::serialization::save_construct_data(Archive & ar, const derived * t, const unsigned int file_version);
template <class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & boost::serialization::base_object<base>(*this);
//only need to return c, a and b already done for constructor
ar & m_c;
}
double m_a, m_b, m_c;
};
//Save and load the data required for the constructor.
namespace boost { namespace serialization {
template <class Archive>
inline void save_construct_data(
Archive & ar,const derived* d,const unsigned int file_version
)
{
// save data required to construct instance
ar << d->m_a;
ar << d->m_b;
}
template <class Archive>
inline void load_construct_data(
Archive & ar, derived* d,const unsigned int file_version
)
{
double a,b;
ar >> a;
ar >> b;
// invoke inplace constructor to initialize instance of my_class
::new(d) derived(a,b);
}
}
}
//register the derived class with boost.
BOOST_CLASS_EXPORT_GUID(derived, "derived")
int
main (int ac, char **av)
{
std::ofstream ofs("filename");
base* p = new derived(2,3);
// save data to archive
{
boost::archive::text_oarchive oa(ofs);
oa << p;
}
// ... some time later restore the class instance to its orginal state
base* p2;
{
std::ifstream ifs("filename");
boost::archive::text_iarchive ia(ifs);
ia >> p2;
}
derived* d = static_cast<derived*>(p2);
std::cout<<"p2 vals are: "<<d->get_a()<<" "<<d->get_b()<<" "<<d->get_c()<<std::endl;
}
OLD RESPONSE:
Not sure I entirely understood your problem (a fuller example would help me) -
The constructor doesn't usually come into it when you serialize an object: you serialize the raw data?
Do you mean that you don't want to serialize all the raw data of the object, but just want to reconstruct it again when you deserialize the object (using the constructor)? If so then you can do this by seriazing the data that you need for the reconstruction and splitting the save and load operations.
struct my_class
{
my_class(Agent& A, ACTION_MODE const & m)
: m_state(A,M)
{}
private:
State m_state;
friend class boost::serialization::access;
void save(Archive & ar, const unsigned int version) const
{
// note, version is always the latest when saving
Agent tmp_A = m_state.get_A();
ACTION_MODE tmp_m = m_state.get_m();
ar & tmp_A;
ar & tmp_m;
}
template<class Archive>
void load(Archive & ar, const unsigned int version)
{
Agent tmp_A;
ACTION_MODE tmp_m
ar & tmp_A;
ar & tmp_m;
m_state = State(tmp_A,tmp_m);
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
}
Does that help, or have I missed the point?

Boost Serializing of Object containing Map (with object values) and Multimap (with std::string values): what is needed?

See below a main() and two very simple classes. Then per Boost serialization (and what is shown) my questions are:
1) Does class B need the normal overloaded stream insertion operators '<<' and '>>' to be defined? Currently in my real code it doesn't have these.
2) Does class A in the store() and load() methods need to iterate through the map and multimap containers explicitely, storing/loading their key:value pairs explicitely?
e.g. something like:
void A::store(const char* filename){
std::ofstream ofs(filename);
boost::archive::text_oarchive oa(ofs);
std::map< std::string, B >::iterator it;
BMap.size();
oa << BMap.size();
for( it = BMap.begin(); it != BMap.end(); it++ ){
oa << it->first;
oa << it->second;
}
//similar for strMultimap
}
I assume that I don't need to do this, but am not certain.
3) Assuming class B has only the two data members shown, does it need a default contructor included explicitely? (as opposed to the implicit default constructor)
4) Does B need to have an overide for the comparison operator '>'? I assume that it doesn't since this is a very simple class.
Finally, any other comments per anything that I've failed to cover is appreciated!
Example code for my above questions:
//includes ommitted
int main() {
std::string file("test.dat");
A * pA = new A;
pA->store(file.c_str());
pA->fillMaps();
//release data
pA->load(file.c_str());
return 0;
}
//includes ommitted
class A
{
friend class boost::serialization::access;
public:
std::map< std::string, B > BMap;
std::multimap< std::string, std::string > strMultimap;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & BMap;
ar & strMultimap;
}
void store(const char* filename){
std::ofstream ofs(filename);
boost::archive::text_oarchive oa(ofs);
oa << this;
}
void load(const char* filename){
std::ifstream ifs(filename);
boost::archive::text_iarchive ia(ifs);
ia >> this;
}
void fillMaps(){
//code to allocate B objects and put them in BMap and fill strMultimap with whatever number of key:value pairs
}
class B
{
friend class boost::serialization::access;
public:
std::string str;
unsigned int num;
B::B(void)
: str("a string")
, num(7)
{
}
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & str;
ar & num;
}
}
1) You don't need stream operators for class B, but it does need a serialize() method. I had to wrap the serialization with the BOOST_SERIALIZATION_NVP (name value pair) macro:
ar & BOOST_SERIALIZATION_NVP(someNamedValue); // use this macro for everything you want to name
There might be a way to avoid naming your map, but I don't know how that's done.
2) No, class A doesn't need map-specific serialization code. Just make sure you include <boost/serialization/map.hpp>.
3) The implicit default constructor should be fine. You only ever need an explicit default constructor if a) you've already provided a non-default constructor or b) you want to change the behavior of the default constructor.
4) No operator < is needed :)
Here's some sample code which compiled, but I haven't run:
#include <boost/serialization/map.hpp>
struct A
{
struct B
{
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
}
};
typedef std::map<int, SomeClass> MyMap;
MyMap myMap;
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_NVP(myMap);
}
};