Is it possible in boost::serialization library to deserialize (polymorphic) objects with references and no default constructor?
class Example
{
int& value;
public:
Example(int _value): value(_value) {}
virtual ~Example() {}
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive & ar, const unsigned int file_version)
{
ar & value;
}
};
class Usage
{
Example* example;
public:
Usage(): example(new Example(123)) {}
~Usage() { delete example; }
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive & ar, const unsigned int file_version)
{
ar & example;
}
};
...
// serialize and deserialize object with reference and no default constructor
{
Usage source;
std::ostringstream oss;
boost::archive::text_oarchive oa(oss);
oa & source;
Usage target;
std::istringstream iss(oss.str());
boost::archive::text_iarchive ia(iss);
ia & target; // does not compile
}
As for non-default constructible object, I'd recommend to look the item
Non-Default Constructors
here.
Your class can be serialized by
writing your own function template load_construct_data and save_construct_data.
Related
The code below is my current thinking to permit boost::serialization of an immutable abstract base with virtual inheritance. I hope I missed something and there is a simpler solution...?
As it stands, it raises a few questions:
Is the comment in IObject::serialize valid?
The comment in bs::save_construct_data for House seems to indicate a bug in boost::serialization. Is that correct, or is there a better way to do this?
Is there a more elegant way to deserialise Building then the Deserialise function combined with a protected constructor?
The result of (3) is that another Building implementation will necessitate a chunk of duplicated code. I suspect this will require a bit of CRTP to mitigate - any alternatives?
How does one make this work if the virtual base contains data members? I suspect this is similar (or identical) to the Building::Deserialise.
#include <fstream>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/export.hpp>
namespace bs = boost::serialization;
// IBase comes from an external library, and we are not interested in
// serialising an IBase*.
class IBase {
public:
virtual ~IBase(){};
};
class IObject : public virtual IBase {
private:
friend class bs::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
std::cout << "Called IObject's serialize\n";
// IBase contains no members so there is no need to serialise it to/from the
// archive. However, the inheritance relationship must be registered in
// boost::serialization. We cannot use base_object to do this: It will try
// to static_cast *this to IBase, but we might not have created the instance
// yet, in which case there is no virtual table and a structured exception
// will be generated.
bs::void_cast_register<IObject, IBase>(static_cast<IObject *>(nullptr),
static_cast<IBase *>(nullptr));
}
public:
virtual ~IObject() {}
};
class IBuilding : public virtual IBase {
private:
friend class bs::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
std::cout << "Called IBuilding's serialize\n";
bs::void_cast_register<IBuilding, IBase>(static_cast<IBuilding *>(nullptr),
static_cast<IBase *>(nullptr));
}
public:
virtual ~IBuilding() {}
};
/* Tedious forward declarations to permit later friending. */
class Building;
class House;
namespace boost {
namespace serialization {
template <class Archive>
inline void save_construct_data(Archive &ar, const Building *t,
const unsigned int version);
template <class Archive>
inline void save_construct_data(Archive &ar, const House *t,
const unsigned int version);
template <class Archive>
inline void load_construct_data(Archive &ar, House *t,
const unsigned int version);
}
}
/* Tedious forward declarations end. */
class Building : public IBuilding, public IObject {
private:
friend class bs::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
std::cout << "Called Building's serialize\n";
// We can use base_object here because although the instance might not be
// created, the memory has been allocated. Since there is no virtual
// inheritance, the static_cast can succeed.
ar &bs::make_nvp("IObject", bs::base_object<IObject>(*this));
ar &bs::make_nvp("IBuilding", bs::base_object<IBuilding>(*this));
}
template <class Archive>
inline friend void bs::save_construct_data(Archive &ar, const Building *t,
const unsigned int version);
const double weight_;
protected:
const double height_;
// The Members, associated constructor, and Deserialise facilitate recreating
// this immutable base.
struct Members {
double weight_;
double height_;
};
Building(const Members &members)
: weight_(members.weight_), height_(members.height_) {}
template <class Archive> const Members Deserialise(Archive &ar) const {
double weight;
double height;
ar >> bs::make_nvp("weight_", weight) >> bs::make_nvp("height_", height);
return {weight, height};
}
public:
bool operator==(const Building &other) const {
return weight_ == other.weight_ && height_ == other.height_;
}
virtual double Height() const = 0;
};
class House : public Building {
private:
template <class Archive>
inline friend void bs::save_construct_data(Archive &ar, const House *t,
const unsigned int version);
template <class Archive>
inline friend void bs::load_construct_data(Archive &ar, House *t,
const unsigned int version);
template <class Archive>
explicit House(Archive &ar) : Building(Deserialise(ar)) {}
public:
House(double weight, double height) : Building({weight, height}) {}
virtual double Height() const { return height_; }
};
BOOST_CLASS_EXPORT(House);
namespace boost {
namespace serialization {
template <class Archive>
inline void save_construct_data(Archive &ar, const Building *t,
const unsigned int version) {
std::cout << "Called Building's save_construct_data\n";
ar << make_nvp("weight_", t->weight_) << make_nvp("height_", t->height_);
}
template <class Archive>
inline void bs::save_construct_data(Archive &ar, const House *t,
const unsigned int version) {
std::cout << "Called House's save_construct_data\n";
const auto &base = base_object<const Building>(*t);
ar << make_nvp("Building", base);
// ar << make_nvp("Building", &base); doesn't seem to work.
// Serialising out a reference calls Building's serialize method, the
// save_construct_data is only called for a pointer. This means we
// have to call it explicitly.
save_construct_data(ar, &base, version);
}
template <class Archive>
inline void bs::load_construct_data(Archive &ar, House *t,
const unsigned int version) {
std::cout << "Called House's load_construct_data\n";
ar >> make_nvp("Building", base_object<Building>(*t));
::new (t) House{ar};
}
}
}
int main() {
const char *file_name = "house.ser";
const bool save_first = true;
const House house(45367, 2.43);
std::cout << house.Height() << "\n";
const IObject *iHouse = &house;
if (save_first) {
std::ofstream ofs(file_name);
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(iHouse);
}
IBuilding *iHouse2;
{
std::ifstream ifs(file_name);
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_SERIALIZATION_NVP(iHouse2);
}
if (dynamic_cast<const Building &>(*iHouse) ==
dynamic_cast<const Building &>(*iHouse2))
std::cout << "ok\n";
else
std::cout << "uh oh\n";
return 0;
}
I don't believe that the comment is correct. I believe that the void_cast_register is un-necessary since IBase is known to be a virtual base class of IObject.
Futhermore, if you don't add the serialization_support free functions for IBase, you won't be able to serialise/deserialise an IBase*, only an IObject* (although I think that's fine unless you are managing ownership through the IBase rather than the IObject).
I have base class User which is serializable :
class User
{
public:
User();
std::string GetLogin() const;
void SetLogin(std::string login);
protected:
std::string mLogin;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & mLogin;
}
};
This class can be inherited by other class like this :
class UserA : public User
{
UserA();
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & boost::serialization::base_object<User>(*this);
ar & mIsSomething;
}
bool mIsSomething = true;
}
To handle those user i have a "manager" class which contain a User vector :
class Manager
{
public:
bool Add(User user);
bool Remove(unsigned int index);
private:
std::vector<User> mUsers;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & mUsers;
}
};
So my manager can be filled with UserA , or UserB (never both at the same time). When i retrieve an element from Manager i simply cast it back to the correct child class.
This part is working fine.
But when i want to serialize the Manager class obviously Boost don't know which kind of User i'm trying to serialize and the extra fields from the child class are not serialized.
What are my solution here ?
Does my design is completly wrong ?
Should i specialize my manager class to something like this ?
class Manager
{
bool Add(UserA user);
bool Add(UserB user);
private:
std::vector<UserA> mUsersA;
std::vector<UserB> mUsersB;
}
So my manager can be filled with UserA , or UserB (never both at the same time)
No it can't:
std::vector<User> mUsers;
stores User objects by value. See What is object slicing?.
Thoughts
I'd also suggest templating the Manager on the concrete user type, but seeing how you use an actual type hierarchy, it seems you might be looking to actually use the runtime polymorphism.
Since serializing polymorphic types is somewhat more involved, let me show you a sample.
It also shows how to use e.g. boost::ptr_vector<> to manage the objects while storing them dynamically.
Live1 on Coliru
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/ptr_container/serialize_ptr_vector.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
class User
{
public:
User() {};
virtual ~User() {}
std::string GetLogin() const;
void SetLogin(std::string login);
protected:
std::string mLogin;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/)
{
ar & mLogin;
}
};
class UserA : public User
{
public:
UserA() {};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/)
{
ar & boost::serialization::base_object<User>(*this);
ar & mIsSomething;
}
bool mIsSomething = true;
};
class UserB : public User
{
public:
UserB() {};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/)
{
ar & boost::serialization::base_object<User>(*this);
ar & mIsSomethingElse;
}
bool mIsSomethingElse = true;
};
template <typename Tag>
class UserGen : public User
{
public:
UserGen() {};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/)
{
ar & boost::serialization::base_object<User>(*this);
ar & mIsGen;
}
bool mIsGen = true;
};
struct GenA;
struct GenB;
struct GenC;
BOOST_CLASS_EXPORT(User)
BOOST_CLASS_EXPORT(UserA)
BOOST_CLASS_EXPORT(UserB)
BOOST_CLASS_EXPORT(UserGen<GenA>)
BOOST_CLASS_EXPORT(UserGen<GenB>)
BOOST_CLASS_EXPORT(UserGen<GenC>)
#include <boost/type_index.hpp>
class Manager
{
public:
template <typename User>
bool Add(User const& user) {
mUsers.push_back(new User(user));
return true; // FIXME?
}
bool Remove(unsigned int index) {
if (mUsers.size() > index) {
mUsers.erase(mUsers.begin()+index);
return true;
}
return false;
}
void dump() const {
for (auto& u : mUsers) {
std::cout << "user of type " << boost::typeindex::type_id_runtime(u) << "\n";
}
}
private:
boost::ptr_vector<User> mUsers;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int /*version*/)
{
ar & mUsers;
}
};
#include <sstream>
#include <iostream>
int main() {
std::stringstream ss;
{
Manager man;
man.Add(UserA{});
man.Add(UserB{});
man.Add(UserGen<GenA>{});
man.Add(UserGen<GenB>{});
man.Add(UserGen<GenC>{});
boost::archive::text_oarchive oa(ss);
oa << man;
}
{
boost::archive::text_iarchive ia(ss);
Manager man;
ia >> man;
man.dump();
}
}
Prints
user of type UserA
user of type UserB
user of type UserGen<GenA>
user of type UserGen<GenB>
user of type UserGen<GenC>
1 linking boost 1.59 is somehow failing there :(
Thanks #m.s. for figuring out 1.58 still works
I have the following problem when I try to call method in "TestSerialize" class during serialization process.
Here is my code:
class TestSerialize
{
public:
std::string GetVal() { return Val + "abc"; }
void SetVal(std::string tVal) { Val = tVal.substr(0, 2); }
protected:
std::string Val;
friend class boost::serialization::access;
template<class Archive> void save(Archive & ar, const unsigned int version) const
{
using boost::serialization::make_nvp;
std::string tVal = GetVal(); // Error here
ar & make_nvp("SC", tVal);
}
template<class Archive> void load(Archive & ar, const unsigned int version)
{
using boost::serialization::make_nvp;
std::string tVal;
ar & make_nvp("SC", tVal);
SetVal(tVal);
}
BOOST_SERIALIZATION_SPLIT_MEMBER();
};
int main()
{
TestSerialize tS;
std::ofstream ofs("test.xml");
boost::archive::xml_oarchive oa(ofs, boost::archive::no_header);
oa << BOOST_SERIALIZATION_NVP(tS);
ofs.close();
return 0;
}
The error that I encountered is:
'TestSerialize::GetVal' : cannot convert 'this' pointer from 'const TestSerialize' to 'TestSerialize &'
This error only happens on "save" but not "load"
I wonder why I get this error. I would like to know what Boost.Serialization do such that we have these two different behaviors.
I use Boost Library 1.47.0
save is a const function and can only call other const functions. GetVal isn't. Change it:
std::string GetVal() const { ... }
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?
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);
}
};