deserialization from an abstract base fails for boost serialization - c++

I'm able to serialize an object through an abstract base using boost::serialization. However, when I try to add deserialization, I get compile errors about the abstract base. Here's my serialization code that works:
/*
g++ -Iinclude/ -Llib -lboost_serialization ~/Desktop/ser_ex.cpp -o stest
*/
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <memory>
namespace bser = boost::serialization;
using namespace std;
namespace foo {
class AA
{
public:
virtual void foo() = 0;
virtual ~AA() {}
std::string name;
template<class Archive>
void serialize(Archive & ar, unsigned int )
{
ar & name;
}
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT( AA );
class BB : public AA
{
public:
virtual void foo() = 0;
virtual void bar() = 0;
virtual ~BB () {}
int thing;
template<class Archive>
void serialize(Archive & ar, unsigned int )
{
ar.template register_type< AA >();
ar & boost::serialization::base_object<AA>(*this);
ar & thing;
}
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT( BB );
class CC : public BB
{
public:
virtual void foo() {}
virtual void bar() {}
virtual ~CC() {}
int otherThing;
template<class Archive>
void serialize(Archive & ar, unsigned int )
{
ar.template register_type< BB >();
ar & boost::serialization::base_object<BB>(*this);
ar & otherThing;
}
};
}
BOOST_CLASS_EXPORT_KEY(foo::CC)
BOOST_CLASS_EXPORT_IMPLEMENT(foo::CC)
int main (int , char const **)
{
std::vector< shared_ptr<foo::AA> > vv;
vv.push_back( make_shared<foo::CC>() );
std::ostringstream oss;
boost::archive::binary_oarchive out_archive( oss );
out_archive << vv;
std::cout << oss.str() << std::endl;
}
Just add this line right below the include for binary_oarchive.hpp causes the code not to compile.
#include <boost/archive/binary_iarchive.hpp>
dawilcox-macOS:debug dawilcox$ make foo
dependencies: foo_main.o
building: foo_main.o
In file included from /Users/dawilcox/src/axle/foo_main.cpp:5:
In file included from /usr/local/include/boost/serialization/serialization.hpp:43:
/usr/local/include/boost/serialization/access.hpp:130:17: error: allocating an object of abstract class type 'foo::AA'
::new(t)T;
Boost is complaining that it can't instantiate my base type. That's kind of the point -- it's an abstract base type.
What's the proper way to deserialize an abstract base?

Looks like the thing I have wrong is that I was registering against the base class in the deserialize function. So, I got rid of:
ar.template register_type< AA >();
and changed
ar.template register_type< BB >();
to
ar.template register_type< CC >();
Here's my full code:
/*
g++ -Iinclude/ -Llib -lboost_serialization ~/Desktop/ser_ex.cpp -o stest
*/
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <OMNIMAKE/lib/boost_serialization>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <memory>
namespace bser = boost::serialization;
using namespace std;
namespace foo {
class AA
{
public:
virtual void foo() = 0;
virtual ~AA() {}
std::string name;
template<class Archive>
void serialize(Archive & ar, unsigned int )
{
ar & name;
}
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT( AA );
class BB : public AA
{
public:
virtual void foo() = 0;
virtual void bar() = 0;
virtual ~BB () {}
int thing;
template<class Archive>
void serialize(Archive & ar, unsigned int )
{
ar & boost::serialization::base_object<AA>(*this);
ar & thing;
}
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT( BB );
class CC : public BB
{
public:
virtual void foo() {}
virtual void bar() {}
virtual ~CC() {}
int otherThing;
template<class Archive>
void serialize(Archive & ar, unsigned int )
{
ar.template register_type< CC >();
ar & boost::serialization::base_object<BB>(*this);
ar & otherThing;
}
};
}
BOOST_CLASS_EXPORT_KEY(foo::CC)
BOOST_CLASS_EXPORT_IMPLEMENT(foo::CC)
int main (int , char const **)
{
std::vector< shared_ptr<foo::AA> > vv;
vv.push_back( make_shared<foo::CC>() );
std::ostringstream oss;
boost::archive::binary_oarchive out_archive( oss );
out_archive << vv;
std::cout << oss.str() << std::endl;
}

Related

Boost XML archiving compiling error with polymorphic classes

I am trying to save my files in XML format using boost archiving.
I get these two errors at compile time.
Error C2664 'int boost::mpl::assertion_failed(boost::mpl::assert::type)': cannot convert argument 1 from 'boost::mpl::failed ***********boost::serialization::is_wrapper:: ***********' to 'boost::mpl::assert::type'
Error C2789 'mpl_assertion_in_line_6': an object of const-qualified type must be initialized
This happens because of these two lines.
BOOST_CLASS_EXPORT_GUID(SumKeyframeXYZ, "SumKeyframeXYZ")
BOOST_CLASS_EXPORT_GUID(SumPosition_Channel, "SumPosition_Channel")
This is the Main function.
#include "Container.h"
BOOST_CLASS_EXPORT_GUID(SumKeyframeXYZ, "SumKeyframeXYZ")
BOOST_CLASS_EXPORT_GUID(SumPosition_Channel, "SumPosition_Channel")
int main()
{
std::ofstream ofs("D:\\output.xml");
Container cont;
cont.AddPositionChannel();
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(cont);
return 0;
}
This is how the classes structure looks like.
Container.h
#include <string>
#include "SumChannel.h"
#include "SumPosition_Channel.h"
#include <fstream>
#include <iostream>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/shared_ptr.hpp>
class Container
{
private:
std::string stdstrName;
std::vector<std::shared_ptr<SumChannel>> Channels;
public:
Container() { stdstrName = "Container Name"; }
void AddPositionChannel() {
std::shared_ptr< SumPosition_Channel> posChannel = std::make_shared<SumPosition_Channel>();
Channels.push_back(posChannel);
};
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive & ar,
const unsigned int version) const {
ar & BOOST_SERIALIZATION_NVP(stdstrName);
ar & BOOST_SERIALIZATION_NVP(Channels);
}
};
//////////////////////////////////////////////////////////
SumChannel.h
#include <fstream>
#include <iostream>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/vector.hpp>
class SumChannel
{
public:
float offset;
SumChannel() { offset = 1.0; }
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive & ar,
const unsigned int version) const {
ar & BOOST_SERIALIZATION_NVP(offset);
}
};
SumPosition_Channel
#include "SumKeyframeXYZ.h"
#include "SumChannel.h"
#include <vector>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/vector.hpp>
class SumPosition_Channel : public SumChannel
{
public:
std::vector<SumKeyframeXYZ> keyframes;
void AddKeyframe() { SumKeyframeXYZ key; keyframes.push_back(key); }
private:
friend class boost::serialization::access;
typedef SumChannel _Super;
template<class Archive> void serialize(Archive & ar,
const unsigned int version) const {
ar & boost::serialization::base_object<_Super>(*this);
ar & BOOST_SERIALIZATION_NVP(keyframes);
}
};
KeyFrameBase.h
#include <iostream>
#include <fstream>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
class Keyframebase
{
public:
std::string stdstrName;
float time;
Keyframebase() { stdstrName = "BaseKey"; time = 50.0; }
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive & ar,
const unsigned int version) {
ar & BOOST_SERIALIZATION_NVP(stdstrName);
ar & BOOST_SERIALIZATION_NVP(time);
}
};
SumKeyframeXYZ.h
#include "KeyframeBase.h"
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/vector.hpp>
class SumKeyframeXYZ : public Keyframebase
{
public:
float x;
float y;
float z;
SumKeyframeXYZ() { x = 150.0; y = 200.0; z = 300.0; }
private:
friend class boost::serialization::access;
template<class Archive> void serialize(Archive & ar,
const unsigned int version) const {
ar & boost::serialization::base_object<Keyframebase>(*this);
ar & BOOST_SERIALIZATION_NVP(x);
ar & BOOST_SERIALIZATION_NVP(y);
ar & BOOST_SERIALIZATION_NVP(z);
}
};
The issue with the code was that i was not writing NVP to the base class
I changed the Base class archiving code from
ar & boost::serialization::base_object<_Super>(*this);
to this for all the base classes.
typedef SumChannel _Super;
ar & boost::serialization::make_nvp("SumChannel", boost::serialization::base_object<_Super>(*this));
and removed the const from the serilize function.

Simple extension of boost::archive::text_oarchive

I am trying to add a member variable to a Boost archive class. Following this, I came up with:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
class MyOArchive : public boost::archive::text_oarchive_impl<MyOArchive>
{
public:
bool MyData;
friend class boost::archive::detail::common_oarchive<MyOArchive>;
friend class basic_text_oarchive<MyOArchive>;
//friend class boost::serialization::save_access; // save_access in namespace boost::serialization does not name a type
MyOArchive(std::ostream& os, unsigned int flags = 0) : boost::archive::text_oarchive_impl<MyOArchive>(os, flags) {}
};
class MyClass
{
public:
private:
friend class boost::serialization::access;
double Value;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & Value;
}
};
int main()
{
MyClass myClass;
std::ofstream outputStream("test.txt");
//boost::archive::text_oarchive outputArchive(outputStream);
MyOArchive outputArchive(outputStream);
outputArchive << myClass;
outputStream.close();
return 0;
}
However, I am getting linker errors:
undefined reference to boost::archive::text_oarchive_impl<MyOArchive>::text_oarchive_impl(std::ostream&, unsigned int)
Can anyone explain what is going wrong here? I don't need to reimplement any of the functions of the archive, just attach this member MyData.
Ah. Spotted it.
You need to include the ipps for the basic implementations in exactly one TU that's linked into the end product.
#include <boost/archive/impl/basic_text_oarchive.ipp>
#include <boost/archive/impl/text_oarchive_impl.ipp>
Also, save_access lives in a different castle namespace:
friend class boost::archive::save_access;
Live On Coliru
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/impl/basic_text_oarchive.ipp>
#include <boost/archive/impl/text_oarchive_impl.ipp>
#include <fstream>
class MyOArchive : public boost::archive::text_oarchive_impl<MyOArchive> {
public:
bool MyData;
friend class boost::archive::detail::common_oarchive<MyOArchive>;
friend class basic_text_oarchive<MyOArchive>;
friend class boost::archive::save_access;
MyOArchive(std::ostream &os, unsigned int flags = 0) : boost::archive::text_oarchive_impl<MyOArchive>(os, flags) {}
};
class MyClass {
private:
friend class boost::serialization::access;
double Value;
template <class Archive> void serialize(Archive &ar, unsigned) { ar &Value; }
};
int main() {
MyClass myClass;
{
std::ofstream outputStream("test.txt");
MyOArchive outputArchive(outputStream);
outputArchive << myClass;
}
}
The output is
22 serialization::archive 13 0 0 6.95328877045326431e-310

how to serialize list of type shared_ptr pointer type using boost serialization

i'm trying to serialize class which has member variable of type like std::list> lss
#include <boost/serialization/optional.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/list.hpp>
struct SomeStruct
{
int x;
double y;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &at, const unsigned int version)
{
at & x;
at & y;
}
};
class BuildTest
{
std::list<boost::shared_ptr<SomeStruct *>> lss ;
std::string name;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &at, const unsigned int version)
{
at & lss; // is this sufficient to serialize lss ?
at & name;
}
}
i have added all required header files for boost serialization. There is similar question but with proper answer how do i serialize list variable of type <boost::shared_ptr<void *>>
You want
std::list<boost::shared_ptr<SomeStruct>> lss ;
Not std::list<boost::shared_ptr<SomeStruct*>>
See it Live On Coliru
#include <boost/serialization/optional.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/list.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/make_shared.hpp>
struct SomeStruct {
int x;
double y;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &at, const unsigned int version) {
at & x;
at & y;
}
};
class BuildTest {
public:
std::list<boost::shared_ptr<SomeStruct>> lss ;
std::string name;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &at, const unsigned int version) {
at & lss; // is this sufficient to serialize lss ?
at & name;
}
};
int main() {
BuildTest bt;
bt.name = "Let's try this";
bt.lss = { boost::make_shared<SomeStruct>(), boost::make_shared<SomeStruct>() };
boost::archive::text_oarchive oa(std::cout);
oa << bt;
}

Boost serilaization with shared pointer and templates

I'm new to C++ and how do i serialize the struct having shared pointer and template .
Below is sample code.
#pragma once
#include <boost/serialization/access.hpp>
#include <boost\serialization\string.hpp>
#include <boost\serialization\shared_ptr.hpp>
//Mydata.hpp file
namespace mydata
{
struct MyData
{
std::string name;
std::string type;
std::shared_ptr<MyInfo> myref;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &ar, const unsigned int vs)
{
ar & name;
ar & type;
ar & myref;
}
}
}
now how do i implement in the corresponding Mydata.cpp file for shared pointer ?
That header includes support for boost::shared_ptr, so the following works:
#include <boost/serialization/access.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/smart_ptr/make_shared.hpp>
namespace mydata
{
struct MyInfo
{
std::string info = "extra info";
MyInfo(std::string info) : info(std::move(info)) {}
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &ar, const unsigned int /*version*/)
{
ar & info;
}
};
struct MyData
{
std::string name;
std::string type;
boost::shared_ptr<MyInfo> myref;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive &ar, const unsigned int /*version*/)
{
ar & name;
ar & type;
ar & myref;
}
};
}
int main()
{
using namespace mydata;
MyData data { "this is a name", "this is a type", boost::make_shared<MyInfo>("this is info") };
boost::archive::text_oarchive oa(std::cout);
oa << data;
}
See it Live On Coliru

Boost::serialization and boost::mpi to broadcast derived class via base class pointer

I am trying to use boost::mpi::broadcast to send a derived class to all nodes via a base class pointer. To do this, I am using the boost::serialization library to serialize my classes. My code, however, does not compile and I get the errors "class boost::mpi::packed_skeleton_iarchive’ has no member named ‘append’" and "class boost::mpi::packed_skeleton_iarchive’ has no member named ‘reset’."
Here is the rough source code for the program:
// Base.hpp
#include <boost/serialization/serialization.hpp>
class Base
{
public:
Base() {}
virtual ~Base() {}
virtual void foo() = 0;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize( Archive& /*ar*/, const unsigned int /*version*/ ) {}
}
// Derived.hpp
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/shared_ptr.hpp>
#include <vector>
#include "Base.hpp"
class Derived : public Base
{
public:
Derived( int param );
virtual ~Derived();
void foo();
private:
int param_;
std::vector< boost::shared_ptr > bar_;
friend class boost::serialization::access;
template<class Archive>
void serialize( Archive& ar, const unsigned int /*version*/ )
{
ar & param_;
ar & bar_;
ar & boost::serialization::base_object< Base >( *this );
}
}
namespace boost
{
namespace serialization
{
template<class Archive>
void load_construct_data( Archive& /*ar*/, Derived* d,
const unsigned int /*v*/ )
{
::new(d) Derived( 0 );
}
}
}
BOOST_CLASS_EXPORT_KEY( Derived )
// Derived.cpp
#include "Derived.hpp"
Derived::Derived( int param ) : param_( param ) {}
Derived::~Derived(){}
Derived::foo()
{
// some stuff
}
BOOST_CLASS_EXPORT_IMPLEMENT( Derived )
// Main.cpp
#include <boost/mpi.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/shared_ptr.hpp>
#include "Derived.hpp"
int main( int argc, char* argv[] )
{
boost::shared_ptr< Base > sp;
if ( world.rank() == 0 )
{
sp = boost::shared_ptr< Base >( new Derived( 5 ) );
boost::mpi::broadcast( world, sp, 0 );
// produce some stuff
} else
{
while ( 1 )
{
// consume some stuff
}
}
return 0;
}
BOOST_SERIALIZATION_ASSUME_ABSTRACT( Base )
I found only one Google Groups discussion about this problem but no solution so far https://groups.google.com/forum/#!msg/boost-developers-archive/Ee9_ilEDO7s/cJTy-8v5lEcJ. How can I get this to compile? I am using openmpi 1.2.8-17.4, gcc 4.5.1 and boost 1.54.
I was thankfully able to answer my own question (I think.) Boost doesn't handle through-base-pointer serialization properly when combined with boost::mpi::packed_skeleton_iarchive. The solution is then to use a different kind of archive, e.g. text_iarchive/text_oarchive, instead.
For example, to broadcast:
std::ostringstream oss;
boost::archive::text_oarchive oa(oss);
oa << value;
std::string s = oss.str();
boost::mpi::broadcast(comm, s, root);
And to receive:
std::string s;
boost::mpi::broadcast(comm, s, root);
std::istringstream iss(s);
boost::archive::text_iarchive ia(iss);
ia >> value;