Reputation: 13
I wrote an example program to figure out Boost's serialization library before I implemented it into a project, however, I got some unexplained behavior.
In my example, I had two classes: a generic BaseClass
and a specialized DerivedClass
(analogous to what I plan to use Boost for). BaseClass
only has one member, a string called name
, which defaults to "BaseClass". DerivedClass
publicly inherits BaseClass
, sets name
to something else and has its own unique member, data
.
In the main program, I create a DerivedClass
with data
set to "special cool stuff", and a BaseClass
with name
"regular stuff". I write both of these to a file with boost::archive::text_oarchive
, and read the first object, the DerivedClass
, back twice (recreating the std::ifstream
both times). The first time reading it back, I put it to a BaseClass*
. Calling BaseClass::printData()
(a virtual method that prints the std::typeid
and name
) prints something along the lines of:
--- Storage done, now loading the first object as BaseClass ---
9BaseClass: 0
Next, when I load it as a DerivedClass*
and call DerivedClass::printData()
(which is overridden from BaseClass
to include the member data
in the output) correctly prints:
--- Storage done, now loading the first object as DerivedClass ---
12DerivedClass: DerivedClass AND special cool stuff
Looking in the file I'm writing to, I see this:
22 serialization::archive 15 0 1 0
0 1 0
1 12 DerivedClass 18 special cool stuff 1
2 13 regular stuff
And when I call BaseClass::printData()
on the original, pre-serializing DerivedClass
, I get this:
9BaseClass: DerivedClass
Obviously, the DerivedClass
is being stored correctly. Something about loading it as a BaseClass
to check name
is messing up. I cannot think of why it would give me a std::string
containing 0
.
I'm just starting to learn how to use this library, and most of the similar questions and documentation I've found online have no effect (ie, using BOOST_EXPORT_CLASS
or BOOST_CLASS_TYPE_INFO
, although it could very well be I was using them incorrectly).
Here is my code:
#include <iostream>
#include <fstream>
#include <string>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/nvp.hpp>
#include "baseclass.h"
#include "derivedclass.h"
int main() {
BaseClass* testBase = new BaseClass("regular stuff");
DerivedClass* testDerivate = new DerivedClass("special cool stuff");
testDerivate->BaseClass::printData();
std::cout << std::endl << " --- " << "Storing objects in the file 'output'..." << " --- " << std::endl;
std::ofstream output("storage");
{
boost::archive::text_oarchive boost_out(output);
boost_out << (testDerivate);
testDerivate->printData();
boost_out << (testBase);
testBase->printData();
}
std::cout << std::endl << " --- " << "Storage done, now loading the first object as BaseClass" << " --- " << std::endl;
{
std::ifstream input("storage");
BaseClass* base;
boost::archive::text_iarchive boost_in(input);
boost_in >> (base);
base->printData();
input.close();
}
std::cout << std::endl << " --- " << "Storage done, now loading the first object as DerivedClass" << " --- " << std::endl;
{
std::ifstream input("storage");
DerivedClass* derive;
boost::archive::text_iarchive boost_in(input);
boost_in >> (derive);
derive->printData();
input.close();
}
return 0;
}
#pragma once
#include <string>
#include <iostream>
#include <typeinfo>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
class BaseClass
{
public:
BaseClass() {
name = "BaseClass";
}
BaseClass(std::string custom) {
name = custom;
}
virtual ~BaseClass() {}
virtual void printData() {
std::cout << typeid(*this).name() << ": " << name << std::endl;
}
protected:
std::string name;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version) {
ar & (name);
}
};
#pragma once
#include <string>
#include <iostream>
#include <typeinfo>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/access.hpp>
#include "baseclass.h"
class DerivedClass : public BaseClass
{
public:
DerivedClass() : BaseClass("DerivedClass") {}
DerivedClass(std::string custom) : BaseClass("DerivedClass") {
data = custom;
}
virtual ~DerivedClass() {}
void printData() override {
std::cout << typeid(*this).name() << ": " << name << " AND " << data << std::endl;
}
protected:
std::string data;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version) {
ar & (boost::serialization::base_object<BaseClass>(*this));
ar & (data);
}
};
Sorry if this is a bit long, I wanted to be as descriptive as possible. I'm very new to using Boost and I'm not all that experienced with C++, so even if you just have some general comments on my code, I'd appreciate it.
Upvotes: 1
Views: 458
Reputation: 393674
You're not loading the same types as you're serializing.
So, while can say:
Base* b = new Derived();
boost_out << b;
And deserialize with:
Base* b = nullptr;
boost_in >> b;
You cannot serialize a Derived*
and deserialize it as Base*
or vice versa.
So, if you know the receiving code must support all derived classes, make it explicit and serialize a Base*
.
To let the de-serializing end know what the possible set of derived types is that could be encountered in deserializing a polymorphic base pointer, export the types.
also see https://www.boost.org/doc/libs/1_67_0/libs/serialization/doc/special.html#export:
Including
BOOST_CLASS_EXPORT
in the "a.hpp" header itself as one would do with other serialization traits will make it difficult or impossible to follow the rule above regarding inclusion of archive headers beforeBOOST_CLASS_EXPORT
is invoked. This can best be addressed by usingBOOST_CLASS_EXPORT_KEY
in the header declarations andBOOST_CLASS_EXPORT_IMPLEMENT
in the class definition file.[ ... snip ... ]
Placing
BOOST_CLASS_EXPORT
in library code will have no effect unless archive class headers are also included. So when building a library, one should include all headers for all the archive classes which he anticipates using. Alternatively, one can include headers for just the Polymoprhic [sic] Archives.
See it Live On Wandbox
main.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/nvp.hpp>
//#include <boost/serialization/export.hpp>
#include "baseclass.h"
#include "derivedclass.h"
int main() {
BaseClass* testBase = new BaseClass("regular stuff");
BaseClass* testDerived = new DerivedClass("special cool stuff");
std::cout << std::endl << " --- " << "Storing objects in the file 'output'..." << " --- " << std::endl;
{
std::ofstream output("storage");
boost::archive::text_oarchive boost_out(output);
boost_out << testBase << testDerived;
}
std::cout << std::endl << " --- " << "Storage done, now loading the first object as BaseClass" << " --- " << std::endl;
{
std::ifstream input("storage");
BaseClass* b1;
BaseClass* b2;
boost::archive::text_iarchive boost_in(input);
boost_in >> b1 >> b2;
std::cout << "b1: "; b1->printData();
std::cout << "b2: "; b2->printData();
}
}
baseclass.h
#pragma once
#include <string>
#include <iostream>
#include <typeinfo>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/export.hpp>
class BaseClass
{
public:
BaseClass() {
name = "BaseClass";
}
BaseClass(std::string custom) {
name = custom;
}
virtual ~BaseClass() {}
virtual void printData() {
std::cout << typeid(*this).name() << ": " << name << std::endl;
}
protected:
std::string name;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, unsigned) {
ar & (name);
}
};
BOOST_CLASS_EXPORT_KEY2(BaseClass, "BaseClass");
baseclass.cpp
#include "baseclass.h"
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
BOOST_CLASS_EXPORT_IMPLEMENT(BaseClass)
derivedclass.h
#pragma once
#include <string>
#include <iostream>
#include <typeinfo>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/export.hpp>
#include "baseclass.h"
class DerivedClass : public BaseClass
{
public:
DerivedClass() : BaseClass("DerivedClass") {}
DerivedClass(std::string custom) : BaseClass("DerivedClass") {
data = custom;
}
virtual ~DerivedClass() {}
void printData() override {
std::cout << typeid(*this).name() << ": " << name << " AND " << data << std::endl;
}
protected:
std::string data;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, unsigned) {
ar & (boost::serialization::base_object<BaseClass>(*this));
ar & (data);
}
};
BOOST_CLASS_EXPORT_KEY2(DerivedClass, "DerivedClass");
derivedclass.cpp
#include "derivedclass.h"
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
BOOST_CLASS_EXPORT_IMPLEMENT(DerivedClass)
Output:
Upvotes: 1