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