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
Related
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()
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.
I don't really know how to explain this.
I have 2 classes World and Entity in the namespace:
artemis::system;
and one class Component in
artemis::component;
I already had trouble with World and Entity, when trying compile it said either one of them wasn't a declared type. ( they both include each other)
So in both classes, within the the namespace, I've added a forward declaration. This seems to solve the problem.
Now when I try to include "world" in my Component.h it gives me the following error:
component is not a namespace-name
My Component class resides within the namespace artemis::component.
This is driving me crazy. I really don't get what is causing this error.
Header Component.h
#ifndef _COMPONENT_H_
#define _COMPONENT_H_
#include <bitset>
#include "BitSize.h"
#include "World.h"
#include <unordered_map>
#include <typeinfo>
#include <string>
#include <iostream>
#include <assert.h>
//#include "EntityManager.h"
using namespace std;
using namespace artemis::system;
namespace artemis {
namespace component {
class Component {
public:
virtual ~Component() = 0;
protected:
Component(){};
};
/**
* Identifies a bitset and id for a component object
* Do not instantiate a ComponentType, instead use the ComponentTypeManager.
* */
class ComponentType {
public:
ComponentType();
bitset<BITSIZE> getBit() const;
int getId() const;
private:
static bitset<BITSIZE> nextBit;
static int nextId;
bitset<BITSIZE> bit;
int id;
void init();
};
//Surpress unused variable warnning. Might need to rewrite it
//#pragma GCC diagnostic push
//#pragma GCC diagnostic ignored "-Wunused-variable"
/**
* Manages the id and bitset for every component based on their type.
* */
class ComponentTypeManager {
private:
ComponentTypeManager();
static unordered_map<size_t,ComponentType*> componentTypes;
public:
/**
*
**/
static ComponentType & getTypeFor(const type_info &t);
/**
* Gets the component type object
**/
template<typename c>
static ComponentType & getTypeFor() {
//Check if we are being legal with components and shizzle
//Component * c = (component*)0;
assert((std::is_base_of< Component, c >::value == true));
return getTypeFor(typeid(c));
}
/**
* Gets the bit set of a component
**/
template<typename c>
static bitset<BITSIZE> getBit() {
//Check if we are being legal with components and shizzle
//Component * c = (component*)0;
assert((std::is_base_of< Component, c >::value == true));
ComponentType & type = getTypeFor(typeid(c));
return type.getBit();
}
/**
* Gets the component id
**/
template<typename c>
static int getId() {
//Check if we are being legal with components and shizzle
assert((std::is_base_of< Component, c >::value == true));
ComponentType & type = getTypeFor(typeid(c));
return type.getId();
};
//typedef getCompBit bitset<BITSIZE>(*getBit<Component>)();
};
//#pragma GCC diagnostic pop
/*template<typename T>
class ComponentMapper {
private:
//ComponentType * type;
EntityManager * em;
public:
ComponentMapper(World &world) {
em = world.getEntityManager();
//type = ComponentTypeManager::getTypeFor<T>();
}
~ComponentType() {
type = nullptr;
em = nullptr;
}
T & get(Entity * e) {
return &(T)em->getComponent<T>(e);
}
};*/
};
};
#endif
Header Entity.h
#ifndef _ENTITY_H
#define _ENTITY_H
#include <bitset>
#include <string>
#include <cstddef>
#include <typeinfo>
#include "BitSize.h"
#include "Component.h"
#include "ImmutableBag.h"
#include "World.h"
//#include "EntityManager.h"
using namespace artemis::util;
using namespace artemis::component;
//using namespace artemis;
using namespace std;
namespace artemis {
namespace system {
class World;
class Entity {
private:
int id;
long int uniqueId;
bitset<BITSIZE> typeBits;
bitset<BITSIZE> systemBits;
World * world;
//EntityManager * entityManager;
protected:
public:
Entity(World * world, int id);
~Entity();
int getID();
void setUniqueId(long int uniqueId);
long int getUniqueID();
bitset<BITSIZE> getTypeBits();
void addTypeBit(bitset<BITSIZE> bit);
void removeTypeBit(bitset<BITSIZE> bit);
bitset<BITSIZE> getSystemBits();
void addSystemBit(bitset<BITSIZE> bit);
void removeSystemBit(bitset<BITSIZE> bit);
void setSystemBits(bitset<BITSIZE> systemBits);
void setTypeBits(bitset<BITSIZE> typeBits);
void reset();
string toString();
void addComponent(Component * c);
//Might change to non template
template<typename c>
void removeComponent() {
//entityManager->removeComponent(this,ComponentTypeManager::getTypeFor<c>());
}
void removeComponent(ComponentType & type);
bool isActive();
Component * getComponent(ComponentType & type);
/*template<typename c>
Component * getComponent() {
return (c)entityManager->getComponent(ComponentTypeManager.getTypeFor<c>());
}*/
ImmutableBag<Component*> * getComponents();
void refresh();
void remove();
void setGroup(string group);
void setTag(string tag);
};
};
};
#endif // $(Guard token)
Header World.h
#ifndef _WORLD_H_
#define _WORLD_H_
//#include "SystemManager.h"
//#include "EntityManager.h"
#include "ImmutableBag.h"
#include "Entity.h"
using namespace artemis::util;
namespace artemis {
namespace system {
class Entity;
class World {
public:
World();
~World();
//SystemManager * getSystemManager();
//EntityManager * getEntityManager();
//TagManager * getTagManager();
//GroupManager * getGroupManager();
int getDelta();
void setDetla(int delta);
void deleteEntity(Entity *e);
void refreshEntity(Entity *e);
//Entity* createEntity();
//Entity* getEntity(int entityID);
void loopStart();
private:
//SystemManager * systemManager;
//EntityManager * entityManager;
//TagManager * tagManager;
//GroupManager * grouManager;
int delta;
Bag<Entity*> refreshed;
Bag<Entity*> deleted;
};
};
};
#endif // $(Guard token)
Your problem is that you require Component to already be defined in order to create World, and you require World to be defined in order to create Component. This is impossible; each one's definition requires the other.
You need to restructure your classes so that you either don't require Component to be defined in World's header, OR you don't require World to be define in Component's header. You can always include them in the .cpp, but not the header. It's a circular inclusion problem. You could also forward-declare one of the classes.
You are essentially saying: I cannot create A unless I have already created B; I cannot create B unless I have already created A. That's impossible, and the compiler is telling you so.
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:))