I am experiencing problems serializing a polymorphic type. Actually I just split the example in: http://uscilab.github.io/cereal/polymorphism.html in several files. It compiles just fine but in runtime i get an exception telling me that I cannot serialize a polymorphic type which is not registered when reaching this line in the code:
oarchive( ptr1, ptr2 );
which is supposed to serialize the contents of ptr1 and ptr2 to an stream.
I attach the files so that anybody can see what's going on.
Thanks in advance for your time!
Best,
Roger.
////////////// IBaseClass.h
#ifndef _IBASECLASS_H_
#define _IBASECLASS_H_
// A pure virtual base class
class IBaseClass
{
public:
virtual void sayType() = 0;
};
#endif
////////////// DerivedClass.h
#ifndef DERIVEDCLASS_H_
#define DERIVEDCLASS_H_
#include "IBaseClass.h"
#include <cereal/types/polymorphic.hpp>
class DerivedClass : public IBaseClass {
void sayType();
int x;
template<class Archive>
void serialize( Archive & ar )
{ ar( x ); }
};
#include <cereal/archives/binary.hpp>
#include <cereal/archives/xml.hpp>
#include <cereal/archives/json.hpp>
// Register DerivedClassOne
CEREAL_REGISTER_TYPE(DerivedClass);
#endif /* DERIVEDCLASS_H_ */
////////////// DerivedClass2.h
#ifndef DERIVEDCLASS2_H_
#define DERIVEDCLASS2_H_
#include "IBaseClass.h"
#include <cereal/types/polymorphic.hpp>
class DerivedClass2 : public IBaseClass {
void sayType();
float y;
template<class Archive>
void serialize( Archive & ar )
{ ar( y ); }
};
#include <cereal/archives/binary.hpp>
#include <cereal/archives/xml.hpp>
#include <cereal/archives/json.hpp>
CEREAL_REGISTER_TYPE(DerivedClass2);
////////////// main.cpp
#include "DerivedClass.h"
#include "DerivedClass2.h"
#include <iostream>
#include <fstream>
#include <memory>
#include <cereal/archives/xml.hpp>
#include <cereal/types/polymorphic.hpp>
int main(int argc, char* argv[])
{
{
std::ofstream os( "polymorphism_test.xml" );
cereal::XMLOutputArchive oarchive( os );
// Create instances of the derived classes, but only keep base class pointers
std::shared_ptr<IBaseClass> ptr1 = std::make_shared<DerivedClass>();
std::shared_ptr<IBaseClass> ptr2 = std::make_shared<DerivedClass2>();
oarchive( ptr1, ptr2 );
}
{
std::ifstream is( "polymorphism_test.xml" );
cereal::XMLInputArchive iarchive( is );
// De-serialize the data as base class pointers, and watch as they are
// re-instantiated as derived classes
std::shared_ptr<IBaseClass> ptr1;
std::shared_ptr<IBaseClass> ptr2;
iarchive( ptr1, ptr2 );
// Ta-da! This should output:
ptr1->sayType(); // "DerivedClassOne"
ptr2->sayType(); // "EmbarrassingDerivedClass. Wait.. I mean DerivedClassTwo!"
}
return 0;
}
https://uscilab.github.io/cereal/polymorphism.html
As you are not doing any serialisation on cereal::base_class(this), there is no path from your derived classes to the base classes. Try adding:
CEREAL_REGISTER_POLYMORPHIC_RELATION(BaseClass, DerivedClassOne)
CEREAL_REGISTER_POLYMORPHIC_RELATION(BaseClass, EmbarrassingDerivedClass)
Related
I'm using boost to serialize object without a default constructor, however I get a weird issue : save_construct_data is not being called !
Bellow a sample program that reproduce this problem:
main.cpp
#include <vector>
#include <iostream>
#include "Test.h"
#include <fstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
int main()
{
using T = float;
walid::Test<T> instance ( 2, {2.3f, -0.5f} ) ;
std::ofstream ofs ( "data" );
boost::archive::text_oarchive oa ( ofs );
oa << instance;
}
Test.h
#ifndef __Test_HEADER__
#define __Test_HEADER__
#include <list>
#include <vector>
#include <iostream>
#include <initializer_list>
namespace walid
{
template<typename T>
class Test
{
public:
std::vector<T> elements;
int in_dim ;
Test(int input_dim, std::initializer_list<T> elem)
{
in_dim = input_dim;
elements = std::vector<T>(elem);
}
void start(){};
void stop(){};
};
}
#include "Test_serialization.inl"
#endif
and finally Test_serialization.inl
namespace boost {
namespace serialization {
template<class Archive, class T>
inline void serialize(Archive & ar, walid::Test<T>& t, const unsigned int version)
{
std::cout<<"serialize Test ..."<<std::endl;
}
template<class Archive, class T>
inline void save_construct_data ( Archive & ar, const walid::Test<T>* t, const unsigned int version )
{
std::cout<<"call save_construct_data Test ..."<<std::endl;
}
template<class Archive, class T>
inline void load_construct_data ( Archive & ar, walid::Test<T>* t, const unsigned int version )
{
std::cout<<"call load_construct_data Test ..."<<std::endl;
::new ( t ) walid::Test<T> ( 2, {2.3f, -0.5f} ) ;
}
}
}
this code is supposed to print :
serialize Test ...
call save_construct_data Test ...
but it is only printing serialize Test ... (save_construct_data is not called)
Am I missing something ?
Thanks for your help.
The assumption appears to be that you are constructing walid::Test<T>.
However, if you look closely, you will realize that you don't actually construct anything.
By design Boost Serialization (de)serializes (into) lvalues. The only place where construction is required during de-serialization is when serializing dynamically allocated objects.
You can convince yourself by changing the storage of the instance to e.g. shared_ptr:
Live On Coliru
#include <fstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/shared_ptr.hpp>
int main()
{
using T = float;
using Test = walid::Test<T>;
{
boost::shared_ptr<Test> shared_instance(new Test(2, {2.3f, -0.5f}));
std::ofstream ofs("data");
boost::archive::text_oarchive oa(ofs);
oa << shared_instance;
}
{
boost::shared_ptr<Test> deserialized;
std::ifstream ifs("data");
boost::archive::text_iarchive ia(ifs);
ia >> deserialized;
}
}
Prints
call save_construct_data Test ...
serialize Test ...
call load_construct_data Test ...
serialize Test ...
I am on my second attempt to setup polymorphic serialization using the boost library. I am using this as a learning experience, but I may be a little in over my head and I am considering going back to coding the serialization myself rather than boost. Or switch to learning the vistor message Sehe showed me in a previous post.
The issue I am seeing is "unregistered void cast"
I am using shared library linking for the boost serialization library
aTodo.h:
#ifndef ATODO_H
#define ATODO_H
#include <boost/serialization/export.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
class aTodo{
public:
static const unsigned _Type=0x00;
virtual ~aTodo(){};
virtual void Do()=0;
virtual unsigned getInitType(){return _Type;};
private:
friend class boost::serialization::access;
template <class Ar> void serialize(Ar &, unsigned){};
};
#endif
todoExec.h:
#ifndef ATODOEXEC_H
#define ATODOEXEC_H
#include "aTodo.h"
class todoExec : public aTodo{
public:
static const unsigned _TYPE= 0x01;
todoExec(std::string const & command=""):_command(command){};
virtual unsigned getInitType(){return _TYPE;};
virtual void Do(){std::cout << "foo:" << getCommand() << std::endl;};
std::string getCommand() const {return _command;};
protected:
private:
friend class boost::serialization::access;
template <class Archive> void serilize(Archive & ar, unsigned){
boost::serialization::void_cast_register<todoExec,aTodo>();
boost::serialization::base_object<aTodo>(*this);
ar& _command;
}
std::string _command;
};
#endif
todoFactory.h:
#ifndef TODOFACTORY_H
#define TODOFACTORY_H
#include "todoExec.h"
#include <memory>
class todoFactory{
todoFactory()=default;
public:
static std::unique_ptr<todoFactory> create(){return std::move(std::unique_ptr<todoFactory>(new todoFactory));};
//save
static std::string save(std::unique_ptr<aTodo> &todoIn){
std::string out;
{
boost::iostreams::stream<boost::iostreams::back_insert_device<std::string>>os(out);
boost::archive::text_oarchive archive(os);
archive << todoIn;
}
return out;
}
static std::unique_ptr<aTodo> load(std::string const &s ){
std::unique_ptr<aTodo> p;
{
boost::iostreams::stream<boost::iostreams::array_source> is(boost::iostreams::array_source{s.data(),s.size()});
boost::archive::text_iarchive archive(is);
archive >> p;
}
return std::move(p);
}
std::unique_ptr<aTodo> createExec(std::string command) {return std::unique_ptr<aTodo>(new todoExec(command));};
};
#endif
client.cpp
#include <string>
#include <iostream>
#include "todoFactory.h"
BOOST_SERIALIZATION_ASSUME_ABSTRACT(aTodo)
BOOST_CLASS_EXPORT(todoExec)
#include <memory>
int main(void)
{
char mtype=0x01;
std::string dataToSend = "ls -al /home/ajonen";
auto tmpTodoFactory=todoFactory::create(); //create factory
auto anExecTodo=tmpTodoFactory->createExec(dataToSend); //create ExecTodo from factory
std::string toSend= tmpTodoFactory->save(anExecTodo);
return 0;
}
The error I get is:
terminate called after throwing an instance of 'boost::archive::archive_exception'
what(): unregistered void cast 8todoExec<-5aTodo
Aborted
In class todoExec you've got a typo - is: serilize, should be: serialize; therefore the cast is not registered.
Since many days I'm trying to solve dll-serialization-problem of a derived nested class. I've tried out a lot of examples I've found ( i.e export_keys), but I allways get runtime error. There is some problems with access to serialization functions. I've wrote a simplified class similar to classes we use according to boost serialization tests (polymorphic_derived2).
// ############# dll ###################
// PlugInClass_Derived_2.h //
#pragma once
#ifdef PLUGIN
// All functions in this file are exported
#else
// All functions in this file are imported
#define PLUGIN __declspec(dllimport)
#endif
#include "PlugInClass_Derived_1.h"
class PlugInClass_Derived_2 : public PlugInClass_Derived_1
{
public:
class DLL_DECL(BOOST_PP_EMPTY()) Parameter : public PlugInClass_Derived_1::Parameter
{
public:
Parameter(void);
virtual ~Parameter(void){};
private:
friend class boost::serialization::access;
template <typename Archive>
void serialize(Archive &ar, const unsigned int version);
public:
virtual const char * get_key() const {
return "PlugInClass_Derived_2_Parameter";
}
};
public:
/** #brief standard constructor */
PlugInClass_Derived_2(){};
virtual ~PlugInClass_Derived_2(){};
PLUGIN virtual boost::shared_ptr<PlugInClass_Base::Parameter> getInitParameter();
};
extern "C" PLUGIN PlugInClass_Base* Create(/*void** params, const int number*/);
extern "C" PLUGIN void Destroy(PlugInClass_Base* p);
// we use this because we want to assign a key to this type
// but we don't want to explicitly instantiate code every time
// we do so!!!. If we don't do this, we end up with the same
// code in BOTH the DLL which implements polymorphic_derived2
// as well as the main program.
BOOST_CLASS_EXPORT_KEY(PlugInClass_Derived_2::Parameter)
// note the mixing of type_info systems is supported.
BOOST_CLASS_TYPE_INFO(
PlugInClass_Derived_2::Parameter,
boost::serialization::extended_type_info_typeid<PlugInClass_Derived_2::Parameter>
)
#undef DLL_DECL
// PlugInClass_Derived_2.cpp
/* boost headers go here */
/* plugin headers go here */
#define POLYMORPHIC_DERIVED2_EXPORT
#include "PlugInClass_Derived_2.h"
PlugInClass_Derived_2::Parameter::Parameter(){
d = -2.;
/*nothing*/
}
template <typename Archive>
void PlugInClass_Derived_2::Parameter::serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<PlugInClass_Derived_1::Parameter>(*this);
}
boost::shared_ptr<PlugInClass_Base::Parameter> PlugInClass_Derived_2::getInitParameter(){
// get local data pointer;
boost::shared_ptr<PlugInClass_Base::Parameter> localParameter(new PlugInClass_Derived_2::Parameter());
// update parent information
// return local data
return localParameter;
}
PlugInClass_Base* Create(/*void** params, const int number*/) {
return new PlugInClass_Derived_2();
}
void Destroy(PlugInClass_Base* p) {
delete p;
}
// instantiate code for text archives
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
template EXPORT_DECL(void) PlugInClass_Derived_2::Parameter::serialize(
boost::archive::text_oarchive & ar,
const unsigned int version
);
template EXPORT_DECL(void) PlugInClass_Derived_2::Parameter::serialize(
boost::archive::text_iarchive & ar,
const unsigned int version
);
// instantiate code for polymorphic archives
#include <boost/archive/polymorphic_iarchive.hpp>
#include <boost/archive/polymorphic_oarchive.hpp>
template EXPORT_DECL(void) PlugInClass_Derived_2::Parameter::serialize(
boost::archive::polymorphic_oarchive & ar,
const unsigned int version
);
template EXPORT_DECL(void) PlugInClass_Derived_2::Parameter::serialize(
boost::archive::polymorphic_iarchive & ar,
const unsigned int version
);
// MWerks users can do this to make their code work
BOOST_SERIALIZATION_MWERKS_BASE_AND_DERIVED(
PlugInClass_Derived_1::Parameter
, PlugInClass_Derived_2::Parameter)
// note: export has to be AFTER #includes for all archive classes
BOOST_CLASS_EXPORT_IMPLEMENT(PlugInClass_Derived_2::Parameter)
// PlugInClass_Derived_1.h
#pragma once
#ifdef PLUGIN
// All functions in this file are exported
#else
// All functions in this file are imported
#define PLUGIN __declspec(dllimport)
#endif
#include "PlugInClass_Base.h"
class PlugInClass_Derived_1 : public PlugInClass_Base
{
public:
class DLL_DECL(BOOST_PP_EMPTY()) Parameter : public PlugInClass_Base::Parameter
{
public:
int a, b, c;
Parameter(void);
virtual ~Parameter(void){};
private:
friend class boost::serialization::access;
template <typename Archive>
void serialize(Archive &ar, const unsigned int version);
public:
virtual const char * get_key() const {
return "PlugInClass_Derived_1_Parameter";
}
};
public:
/** #brief standard constructor */
PlugInClass_Derived_1(){};
virtual ~PlugInClass_Derived_1(){};
virtual boost::shared_ptr<PlugInClass_Base::Parameter> getInitParameter();
};
// we use this because we want to assign a key to this type
// but we don't want to explicitly instantiate code every time
// we do so!!!. If we don't do this, we end up with the same
// code in BOTH the DLL which implements polymorphic_derived2
// as well as the main program.
BOOST_CLASS_EXPORT_KEY(PlugInClass_Derived_1::Parameter)
// note the mixing of type_info systems is supported.
BOOST_CLASS_TYPE_INFO(
PlugInClass_Derived_1::Parameter,
boost::serialization::extended_type_info_typeid<PlugInClass_Derived_1::Parameter>
)
// PlugInClass_Derived_1.cpp
/* boost headers go here */
/* plugin headers go here */
#define POLYMORPHIC_DERIVED2_EXPORT
#include "PlugInClass_Derived_1.h"
PlugInClass_Derived_1::Parameter::Parameter():
a(1),
b(2),
c(3)
{
d = -1.;
/*nothing*/
}
template <typename Archive>
void PlugInClass_Derived_1::Parameter::serialize(Archive &ar, const unsigned int version)
{
ar & boost::serialization::base_object<PlugInClass_Base::Parameter>(*this);
ar & a;
ar & b;
ar & c;
}
boost::shared_ptr<PlugInClass_Base::Parameter> PlugInClass_Derived_1::getInitParameter(){
// get local data pointer;
boost::shared_ptr<PlugInClass_Base::Parameter> localParameter(new PlugInClass_Derived_1::Parameter());
// update parent information
// return local data
return localParameter;
}
// instantiate code for text archives
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
template EXPORT_DECL(void) PlugInClass_Derived_1::Parameter::serialize(
boost::archive::text_oarchive & ar,
const unsigned int version
);
template EXPORT_DECL(void) PlugInClass_Derived_1::Parameter::serialize(
boost::archive::text_iarchive & ar,
const unsigned int version
);
// instantiate code for polymorphic archives
#include <boost/archive/polymorphic_iarchive.hpp>
#include <boost/archive/polymorphic_oarchive.hpp>
template EXPORT_DECL(void) PlugInClass_Derived_1::Parameter::serialize(
boost::archive::polymorphic_oarchive & ar,
const unsigned int version
);
template EXPORT_DECL(void) PlugInClass_Derived_1::Parameter::serialize(
boost::archive::polymorphic_iarchive & ar,
const unsigned int version
);
// MWerks users can do this to make their code work
BOOST_SERIALIZATION_MWERKS_BASE_AND_DERIVED(
PlugInClass_Base::Parameter
, PlugInClass_Derived_1::Parameter)
// note: export has to be AFTER #includes for all archive classes
BOOST_CLASS_EXPORT_IMPLEMENT(PlugInClass_Derived_1::Parameter)
// PlugInClass_Base.h
#pragma once
#ifdef PLUGIN
// All functions in this file are exported
#else
// All functions in this file are imported
#define PLUGIN __declspec(dllimport)
#endif
// boost headers
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/tracking.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/type_info_implementation.hpp>
#include <boost/serialization/extended_type_info_no_rtti.hpp>
#include <boost/serialization/extended_type_info_typeid.hpp>
#include <boost/preprocessor/empty.hpp>
// export_decl, import_decl from http://www.boost.org/doc/libs/1_55_0/libs/serialization/test/
#ifdef BOOST_HAS_DECLSPEC // defined in config system
#if ! defined(EXPORT_DECL)
#if defined(__BORLANDC__)
#define EXPORT_DECL(T) T __export
#else
#define EXPORT_DECL(T) __declspec(dllexport) T
#endif
#endif
#if ! defined(IMPORT_DECL)
#if defined(__BORLANDC__)
#define IMPORT_DECL(T) T __import
#else
#define IMPORT_DECL(T) __declspec(dllimport) T
#endif
#endif
#else
#define IMPORT_DECL(T) T
#define EXPORT_DECL(T) T
#endif // BOOST_HAS_DECLSPEC
#if defined(POLYMORPHIC_DERIVED_IMPORT)
#define DLL_DECL IMPORT_DECL
#elif defined(POLYMORPHIC_DERIVED_EXPORT)
#define DLL_DECL EXPORT_DECL
#else
#define DLL_DECL(x)
#endif
class PlugInClass_Base
{
public:
class DLL_DECL(BOOST_PP_EMPTY()) Parameter
{
public:
double d;
virtual ~Parameter(void){};
private:
friend class boost::serialization::access;
template <typename Archive>
void serialize(Archive &ar, const unsigned int version){
ar & d;
};
public:
virtual const char * get_key() const = 0;
};
public:
/** #brief standard constructor */
PlugInClass_Base(){};
virtual ~PlugInClass_Base(){};
PLUGIN virtual boost::shared_ptr<PlugInClass_Base::Parameter> getInitParameter(){return NULL;};
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(PlugInClass_Base::Parameter)
// we use this because we want to assign a key to this type
// but we don't want to explicitly instantiate code every time
// we do so!!!. If we don't do this, we end up with the same
// code in BOTH the DLL which implements polymorphic_derived2
// as well as the main program.
BOOST_CLASS_EXPORT_KEY(PlugInClass_Base::Parameter)
// note the mixing of type_info systems is supported.
BOOST_CLASS_TYPE_INFO(
PlugInClass_Base::Parameter,
boost::serialization::extended_type_info_typeid<PlugInClass_Base::Parameter>
)
/// ######### end of dll ################### ///
// and at least the main
#include <stdio.h>
#include <tchar.h>
#include <fstream>
#include <sstream>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/tracking.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/type_info_implementation.hpp>
#include <boost/serialization/extended_type_info_no_rtti.hpp>
#include "PlugIn_Derived\PlugInClass_Base.h"
#include <windows.h>
int main(int argc, char** argv)
{
boost::shared_ptr<PlugInClass_Base> dll_;
/// ######### load dll ################### ///
std::string pluginPath_ = "PlugInClass_Derived_2.dll";
HINSTANCE currLibrary = LoadLibrary(pluginPath_.c_str());
if (!currLibrary) {
throw std::runtime_error("The plugin could not be loaded.");
}
// load the plugin class interface from the library using its factory functions
typedef PlugInClass_Base *(*CreatePluginFuncDef)();
CreatePluginFuncDef pluginCreateFactoryPtr = (CreatePluginFuncDef)GetProcAddress(currLibrary, "Create");
typedef void(*DestroyPluginFuncDef)(PlugInClass_Base*);
DestroyPluginFuncDef pluginDestroyPtr = (DestroyPluginFuncDef)GetProcAddress(currLibrary, "Destroy");
// create object within dll
dll_ = boost::shared_ptr<PlugInClass_Base>(pluginCreateFactoryPtr(), pluginDestroyPtr);
/// ######### get object from dll ################### ///
boost::shared_ptr<PlugInClass_Base::Parameter> parameterPtr = dll_->getInitParameter();
/// ######### serialization ################### ///
std::string s;
// create and open a character archive for output
{
std::ostringstream os;
// save data to archive
boost::archive::text_oarchive oa(os);
// write class instance to archive
oa << parameterPtr; // HERE RUNTIME ERROR
// Unhandled exception at xxxx in test_dll_simple_example.exe:
// Microsoft C++ exception: boost::archive::archive_exception at memory location xxxx
s = os.str();
}
// ... some time later restore the class instance to its orginal state
std::system("pause");
return 0;
}
As you can see the class I want so serialize is allways a nested class "Parameter" defined in derived class from "dll"-interface (PlugInClass_Base) and derived from parameter class of base class (i.e PlugInClass_Derived_1::Parameter). The dll-function "getInitParameter" returns the base pointer to the base-class of parameter-class we want to store.
Can you see what I'm doing wrong during the serialization? I would be glad if you can give me some hints.
Alexandros.
I've found out, if the base-pointer is casted to derived pointer, the serialization works.
/// ######### get object from dll ################### ///
boost::shared_ptr<PlugInClass_Base::Parameter> parameterPtr = dll_->getInitParameter();
boost::shared_ptr<PlugInClass_Derived_2::Parameter> derivedParameterPtr = boost::static_pointer_cast<PlugInClass_Derived_2::Parameter>(parameterPtr);
/// ######### serialization ################### ///
std::string s;
// create and open a character archive for output
{
std::ostringstream os;
// save data to archive
boost::archive::text_oarchive oa(os);
oa << derivedParameterPtr; // It works !!!
s = os.str();
}
So, somehow the serialization can't go through all derived pointers in the base pointer.
The base-class is a abstact class
(BOOST_SERIALIZATION_ASSUME_ABSTRACT(PlugInClass_Base::Parameter))
and the keys are exported
BOOST_CLASS_EXPORT_KEY(PlugInClass_Base::Parameter)
and there is implementation of each derived class is exported ( in cpp file )
BOOST_CLASS_EXPORT_IMPLEMENT(PlugInClass_Derived_1::Parameter)
Can it be a problem with keys of nested classes ? Otherwise all derived classes have their own get_key() function.
Any assumptions ?
The last post is wrong.
It works, because for casting of base pointer the derived class has to be linked -> static.
It means dll-serialization still not works
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;
I'm trying to serialize my data structures in order to write them to a tcp socket.
So far I found that my problem is the serialization. I even tried to use
BOOST_SERIALIZATION_ASSUME_ABSTRACT(T)
but I can't find any working example similar to my program and how to implement it correctly.
Here are some of the links that I have visited:
http://programmers-blog.com/category/c-c
http://www.boost.org/doc/libs/1_48_0/libs/serialization/doc/tutorial.html#simplecase
http://en.highscore.de/cpp/boost/serialization.html#serialization_class_hierarchies
My data structures are a little more complicated then this one but let's assume that I have the following structure
Coordinate.h
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
class Coordinate {
public:
Coordinate() {}
Coordinate(int c) : c(c) {}
int get(void) { return c; }
std::string toString(void);
private:
int c;
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive &ar, const unsigned int version) {
ar & this->c;
}
};
Move.h
class Coordinate;
#include "Coordinate.h"
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
class Move {
public:
Move() {}
~Move() {}
Coordinate* getCoordinate(void) {return this->destination; }
virtual bool isJump(void) = 0;
protected:
Coordinate *destination;
private:
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive &ar, const unsigned int version) {
ar & this->destination;
}
};
MoveNormal.h
class Coordinate;
#include "Move.h"
#include "Coordinate.h"
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
class MoveNormal : public Move {
public:
MoveNormal() {}
MoveNormal(Coordinate *destination) { this->destination = destination; }
~MoveNormal() {}
virtual bool isJump(void);
private:
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive &ar, const unsigned int version) {
ar & boost::serialization::base_object<Move>(*this);
}
};
The virtual methods are defined in here.
MoveNormal.cpp
#include "MoveNormal.h"
bool MoveNormal::isJump(void) {
return false;
}
My main.cpp looks like this:
#include "Coordinate.h"
#include "Move.h"
#include "MoveNormal.h"
#include <fstream>
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
int main(int argc, char *argv[]) {
Coordinate *c = new Coordinate(10);
// This runs OK
/*
{
std::ofstream ofs("f.txt");
boost::archive::text_oarchive oa(ofs);
oa << c;
}
Coordinate *d;
{
std::ifstream ifs("f.txt");
boost::archive::text_iarchive ia(ifs);
ia >> d;
}
std::cout << "c.get(): " << c->get() << std::endl;
std::cout << "d.get(): " << d->get() << std::endl;
*/
// This is where I get my error
Move *m = new MoveNormal(c);
{
std::ofstream ofs("f.txt");
boost::archive::text_oarchive oa(ofs);
oa << m; // Line where the error occurs
}
return 0;
}
But when I run the program I get the following error:
Unhandled exception at 0x76dbb9bc in Test.exe: Microsoft C++ exception: boost::archive::archive_exception at memory location 0x001df078..
I'm using VS2010, and Boost 1.48.0.
This is a little bit weird but I'm going to answer my own question. I just figured out how to make my example above work.
Here it goes the solution. Everytime we need to serialize a class that inherits attributes from another class we need to use the macro:
BOOST_CLASS_EXPORT(T)
According to the boost serialization doc
BOOST_CLASS_EXPORT in the same source module that includes any of the archive class headers will instantiate code required to serialize polymorphic pointers of the indicated type to the all those archive classes. If no archive class headers are included, then no code will be instantiated.
Note that the implemenation of this functionality requires that the BOOST_CLASS_EXPORT macro appear after and the inclusion of any archive class headers for which code is to be instantiated.
So in my case my main.cpp file is now:
#include <fstream>
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
#include <boost\serialization\export.hpp>
#include "Coordinate.h"
#include "Move.h"
#include "MoveNormal.h"
BOOST_CLASS_EXPORT(MoveNormal)
int main(int argc, char *argv[]) {
Coordinate *c = new Coordinate(150);
Move *m = new MoveNormal(c);
std::cout << "m.getDestination().get(): " << m->getDestination()->get() << std::endl;
{
std::ofstream ofs("f.txt");
boost::archive::text_oarchive oa(ofs);
oa << m;
}
Move *n;
{
std::ifstream ifs("f.txt");
boost::archive::text_iarchive ia(ifs);
ia >> n;
}
std::cout << "n.getDestination().get(): " << n->getDestination()->get() << std::endl;
return 0;
}
Just make sure that you include all the boost archives you need before you use the export MACRO.
To finish my project besides the serialization I need to write them to a tcp socket using boost::asio.
So let's assume that I have a connection header like this one and that now I have another class called MoveJump defined in my MoveJump.h
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
#include "Coordinate.h"
#include "Move.h"
class MoveJump : public Move {
public:
MoveJump() {}
MoveJump(Coordinate *c) { this->destinatio = c; }
~MoveJump() {}
virtual bool isJump(void);
private:
friend class boost::serialization::access;
template<typename Archive>
void serializize(Archive &ar, const unsigned int version) {
ar & boost::serialization::base_object<Move>(*this);
}
};
Now to serialize these structures my main look like this
#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
#include <boost\serialization\export.hpp>
#include <fstream>
#include "Coordinate.h"
#include "Move.h"
// And now we register all the possible Moves
#include "MoveNormal.h"
BOOST_CLASS_EXPORT(MoveNormal)
#include "MoveJump.h"
BOOST_CLASS_EXPORT(MoveJump)
int main(int argc, char *argv[]) {
Coordinate *c = new Coordinate(10);
Move *m = new MoveNormal(c);
Coordinate *d = new Coordinate(15);
Move *j = new MoveJump(d);
{
std::ofstream ofs("m.txt");
boost::archive::text_oarchive oa(ofs);
oa << m;
}
{
std::ofstream ofs("j.txt");
boost::archive::text_oarchive oa(ofs);
oa << j;
}
}
The trick is to register the classes that will be serialized when we have the pointer to the base class.
If inside my Move.h I have more pointers to other base classes, which I do in my project, we need to include in the main all the headers and register all the possible classes that expand the base class.
I hope this helps someone who might have similar problems in the future.
Feel free to present new possible solutions.
Thanks
General speaking, you can simply use BOOST_CLASS_EXPORT to register all the classes, or you can use BOOST_SERIALIZATION_ASSUME_ABSTRACT for the super class, and use member function register_type of "archive" together. see : How to serialize derived template classes with Boost.serialize? for details.(sorry for my poor english:))