I want to serialize classes that have std::shared_ptr s of each other as members and that are declared and defined in different files. A minimal example of my code would be:
//auxiliary.h
#include <memory>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
class A;
class B;
typedef std::shared_ptr< A > A_ptr;
typedef std::shared_ptr< B > B_ptr;
//A.h
#include "auxiliary.h"
class A{
B_ptr bpt;
void foo();
//...
};
//A.cpp
#include "A.h"
void A::foo()
{
//implementation
}
//B.h
#include "auxiliary.h"
class B{
A_ptr apt;
void bar();
//...
};
//B.cpp
#include "B.h"
void B::bar()
{
//implementation
}
when I try to serialize both classes now by writing
//A.h
#include "auxiliary.h"
class A{
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & B_ptr;
}
B_ptr bpt;
void foo();
//...
};
and
//B.h
#include "auxiliary.h"
class B{
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & A_ptr;
}
A_ptr apt;
void bar();
//...
};
I get the error C2139 "A": an undefined class is not allowed as an argument to compiler intrinsic type __is_base_of
I understand that the compiler would rather see the serialization functions defined in the corresponding cpp files, but that won't work in the usual way as they are templates.
How could I possibly fix the whole thing?
PS.: I also read, that it helped in similar situation to instanciate the template with all the variants one uses and boost states in the manual, that this should be done somewhere via
template void serialize<boost::archive::text_iarchive>(
boost::archive::text_iarchive & ar,
const unsigned int file_version
);
template void serialize<boost::archive::text_oarchive>(
boost::archive::text_oarchive & ar,
const unsigned int file_version
I've tried various ways to do it, but with no success.
Boost Serialization Archives are compile-time polymorphic. This means that they combine the functionality from the class and function templates that define the individual types and behaviours.
Due to the nature of templates in C++ this means that you require the full definition of these templates (and their dependent types) at the POI (point of instantiation). (See Why can templates only be implemented in the header file? for a primer).
Solution?
You can hide the serialization implementation in a TU (translation unit) that does include all the definitions.
Alternatively, you can use Polymorphic Archives. In that case, the serialize methods do not strictly need to be compile-time generic anymore.
Note: the documentation example shows the serialize member as a template function that is explicitly instantiated inside a single TU. This may be required for technical reasons¹, though logically it is completely equivalent to simply declaring two overloads taking polymorphic_[io]archive& in the header and implementing them in the same TU
¹ whether the library internals rely on T::serialize<> to be a template, rather than just letting C++ overload resolution do its job
BONUS
A demo combining a few of those ideas: Live On Wandbox
auxiliary.h
#pragma once
class A;
class B;
#include <memory>
typedef std::shared_ptr<A> A_ptr;
typedef std::shared_ptr<B> B_ptr;
namespace boost { namespace serialization { class access; } }
A.h
#pragma once
#include "auxiliary.h"
class A {
public:
B_ptr bpt;
void foo();
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive&, unsigned);
};
B.h
#pragma once
#include "auxiliary.h"
class B {
public:
A_ptr apt;
void bar();
//...
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive&, unsigned);
};
A.cpp
// A.cpp
#include "A.h"
#include "B.h"
void A::foo() {
// implementation
bpt = std::make_shared<B>();
}
template <class Archive>
void A::serialize(Archive &ar, unsigned)
{
ar & bpt;
}
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "B.h" // required at point of instatiation
template void A::serialize(boost::archive::text_iarchive&, unsigned);
template void A::serialize(boost::archive::text_oarchive&, unsigned);
B.cpp
// B.cpp
#include "B.h"
#include <iostream>
void B::bar() {
// implementation
std::cout << "Hello from B::bar()\n";
}
template <class Archive>
void B::serialize(Archive &ar, unsigned)
{
ar & apt;
}
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "A.h" // required at point of instatiation
template void B::serialize(boost::archive::text_iarchive&, unsigned);
template void B::serialize(boost::archive::text_oarchive&, unsigned);
test.cpp
#include <iostream>
#include <sstream>
void test_serialize(std::ostream&);
void test_deserialize(std::istream&);
int main() {
std::stringstream ss;
test_serialize(ss);
std::cout << ss.str() << std::flush;
test_deserialize(ss);
}
#include "auxiliary.h"
#include <boost/archive/text_oarchive.hpp>
#include "A.h"
//#include "B.h" // optional, see below
void test_serialize(std::ostream& os) {
boost::archive::text_oarchive oa(os);
A a1, a2;
a1.foo();
// a1.bpt->bar(); // only works if B.h included
A a3 = a1; // copy, should alias a1.bpt and a3.bpt
oa << a1 << a2 << a3;
}
#include <boost/archive/text_iarchive.hpp>
#include "B.h" // optional, see below
void test_deserialize(std::istream& is) {
boost::archive::text_iarchive ia(is);
A a1, a2, a3;
ia >> a1 >> a2 >> a3;
std::cout << std::boolalpha;
std::cout << "B correctly deserialized: " << (a1.bpt && !a2.bpt) << "\n";
std::cout << "Correctly aliased a1.bpt == a3.bpt: " << (a1.bpt == a3.bpt) << "\n";
a3.bpt->bar(); // only works if B.h included
}
Prints
/home/sehe/custom/boost_1_65_0/boost/archive/detail/oserializer.hpp:467:22: runtime error: reference binding to null pointer of type 'const struct A'
/home/sehe/custom/boost_1_65_0/boost/archive/detail/oserializer.hpp:467:22: runtime error: reference binding to null pointer of type 'const struct B'
22 serialization::archive 15 1 0
0 0 1 2 1 0
1 0 1 -1
2 -1
3 2 1
/home/sehe/custom/boost_1_65_0/boost/archive/detail/iserializer.hpp:540:19: runtime error: reference binding to null pointer of type 'struct B'
/home/sehe/custom/boost_1_65_0/boost/archive/detail/iserializer.hpp:541:67: runtime error: reference binding to null pointer of type 'const struct B'
B correctly deserialized: true
Correctly aliased a1.bpt == a3.bpt: true
Hello from B::bar()
Related
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)
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'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:))
I have the following situation (simplified):
a.h:
#include <boost/serialisation/serialisation.hpp>
class B;
using namespace std; using namespace boost;
class A {
B *b;
template<class Archive> void serialize(Archive & ar, const unsigned int version) { ar & b; }
};
b.h:
#include <boost/serialisation/serialisation.hpp>
class C;
using namespace std; using namespace boost;
class B {
C *c;
template<class Archive> void serialize(Archive & ar, const unsigned int version) { ar & c; }
};
c.h:
#include <boost/serialisation/serialisation.hpp>
using namespace std; using namespace boost;
class C {
int somevar;
template<class Archive> void serialize(Archive & ar, const unsigned int version) { ar & somevar; }
};
main.cxx:
#include <boost/archive/text_oarchive.hpp>
#include <fstream>
#include "a.h"
#include "b.h"
#include "c.h"
using namespace std; using namespace boost;
int main() {
A a; // Create the object
ofstream ofs("save");
archive::text_oarchive oa(ofs);
oa << a; // Save the object
return 0;
}
Now, the problem is that I have to include the header files of all the classes that I want to serialise in the saving function (in this case right inside main). The problem with that is that there are a lot more than three classes, and they are much more complicated, resulting in a compilation bottleneck at this point.
I only recently begun using boost serialisation, but I've looked over the documentation and searched on both google and here, so I figure I've missed something obvious.
Does anybody have a solution for this such that only "a.h" needs to be included and not "b.h", "c.h", etc?
EDIT: This is hopefully the crucial part of the compilation error that occurs if I comment out the #include "b.h" and "c.h" lines:
main.cxx:17:1: instantiated from here
/usr/include/boost/type_traits/is_abstract.hpp:72:4: error: incomplete type ‘B’ not allowed
I think you could use explicit instantiation, and then define your serialize member functions in .cpp files. Then a.cpp can include b.h and c.h as needed, and users of a.h don't need to do that anymore.
Look into the pimpl-idiom examples http://www.boost.org/doc/libs/1_46_1/libs/serialization/example/demo_pimpl_A.hpp (header) and http://www.boost.org/doc/libs/1_46_1/libs/serialization/example/demo_pimpl_A.cpp (source file) for how to do that.
Disclaimer: I know nothing of boost serialisation.
Since you have template member functions serialize which have to have inline definitions, which are using the members, you should #include the header files for those members instead of just declaring eg class B;.
This will mean that main will only have to #include "a.h" in main and you shouldn't get compiler errors.
First of all, if there is a method, any method, that needs to access A, B and C, there is simply no other way than to make A, B and C visible to that method, and in yur case tha means including all 3 header files. So the compiler bottlenck you talk about cannot be solved.
Second, you should be able to use non-invasive functions for Boost's serialization. Not sure if that solves your particular problem but it's definitely worth a try and it does move all serialization related stuff into one single header file.
//header myserialization.h
#include "a.h"
#include "b.h"
#include "c.h"
template<class Archive>
void serialize(Archive & ar, const unsigned int version, A& a) { ar & a.*b; }
template<class Archive>
void serialize(Archive & ar, const unsigned int version, B& b) { ar & b.*c; }
template<class Archive>
void serialize(Archive & ar, const unsigned int version, C& c) { ar & c.somevar; }