最近在使用Boost 序列化自定义的数据类型时,遇到了一些问题,在这里记录一下解决这些问题的过程。
背景:我有一些类,是多级继承的。例如:class C:pulic class B,class B:pulic class A。这些类中都有虚函数,而且存在使用基类指针指向子类指针对象的情况。这些类是定义在一个DLL中,导出使用。在这些类中添加了Boost序列化代码。一般情况下,在主程序中序列化这些类的非指针对象时没有问题,指针类型和实际指向的指针的类型一致时序列化这个指针也没有问题。当使用基类指针指向子类指针对象时,序列化代码在程序执行时就会抛异常“unregistered class - derived class not registered or exported ”。
开始我怀疑是我多级继承导致的问题。于是就写了一些验证代码。代码如下:
基类头文件:
#include "boost/serialization/split_member.hpp"
#include <boost/serialization/access.hpp>
#include <boost/serialization/export.hpp>
#include "boost/archive/binary_iarchive.hpp"
#include "boost/archive/binary_oarchive.hpp"
namespace CY
{
class CBaseA
{
public:
CBaseA();
virtual ~CBaseA();
virtual void DoNothing() {}
private:
friend class ::boost::serialization::access;
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
ar & a;
}
template<class Archive>
void load(Archive & ar, const unsigned int version) {
ar & a;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
private:
double a;
};
}
子类头文件:
#include <boost/serialization/base_object.hpp>
#include "CBaseA.h"
namespace CY
{
class CDivB : public CBaseA
{
public:
CDivB();
virtual ~CDivB();
private:
friend class ::boost::serialization::access;
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
ar & ::boost::serialization::base_object<CBaseA>(*this);
ar & b;
}
template<class Archive>
void load(Archive & ar, const unsigned int version) {
ar & ::boost::serialization::base_object<CBaseA>(*this);
ar & b;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
private:
double b;
};
}
孙类头文件
#include "CDivB.h"
namespace CY
{
class CDivC : public CDivB
{
public:
CDivC();
virtual ~CDivC();
private:
friend class ::boost::serialization::access;
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
ar & ::boost::serialization::base_object<CDivB>(*this);
ar & c;
}
template<class Archive>
void load(Archive & ar, const unsigned int version) {
ar & ::boost::serialization::base_object<CDivB>(*this);
ar & c;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
private:
double c;
};
}
这些类的CPP文件内容如下:
#include "CBaseA.h"
namespace CY
{
CBaseA::CBaseA()
{
a = 0.1;
}
CBaseA::~CBaseA()
{
}
}
///
#include "boost/archive/binary_iarchive.hpp"
#include "boost/archive/binary_oarchive.hpp"
#include "CDivB.h"
BOOST_CLASS_EXPORT_GUID(CY::CDivB, "CDivB")
namespace CY
{
CDivB::CDivB()
{
b = 0.2;
}
CDivB::~CDivB()
{
}
}
///
#include "CDivC.h"
BOOST_CLASS_EXPORT_GUID(CY::CDivC, "CDivC")
namespace CY
{
CDivC::CDivC()
{
c = 3;
}
CDivC::~CDivC()
{
}
}
在main函数中使用如下代码进行测试:
bool bSave = true;
if (bSave)
{
CBaseA *p = new CDivC();
Save("D:/boost2.bin", p);
}
else
{
CBaseA *p = nullptr;
Load("D:/boost2.bin", p);
printf("aa\n");
}
经过测试,读写都没有问题。
但是一旦把这三个类的定义和实现放到DLL中,序列化时就会抛出异常“unregistered class - derived class not registered or exported ”
DLL版代码如下:
CBaseA.h
#include "boost/serialization/split_member.hpp"
#include <boost/serialization/access.hpp>
#include <boost/serialization/export.hpp>
#include "boost/archive/binary_iarchive.hpp"
#include "boost/archive/binary_oarchive.hpp"
namespace CY
{
class _EXPORTS_API CBaseA
{
public:
CBaseA();
virtual ~CBaseA();
virtual void DoNothing() {}
private:
friend class ::boost::serialization::access;
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
ar & a;
}
template<class Archive>
void load(Archive & ar, const unsigned int version) {
ar & a;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
private:
double a;
};
}
CDivb.h
#include <boost/serialization/base_object.hpp>
#include "CBaseA.h"
namespace CY
{
class _EXPORTS_API CDivB : public CBaseA
{
public:
CDivB();
virtual ~CDivB();
private:
friend class ::boost::serialization::access;
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
ar & ::boost::serialization::base_object<CBaseA>(*this);
ar & b;
}
template<class Archive>
void load(Archive & ar, const unsigned int version) {
ar & ::boost::serialization::base_object<CBaseA>(*this);
ar & b;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
private:
double b;
};
}
CDivC.h
#include "CDivB.h"
namespace CY
{
class _EXPORTS_API CDivC : public CDivB
{
public:
CDivC();
virtual ~CDivC();
private:
friend class ::boost::serialization::access;
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
ar & ::boost::serialization::base_object<CDivB>(*this);
ar & c;
}
template<class Archive>
void load(Archive & ar, const unsigned int version) {
ar & ::boost::serialization::base_object<CDivB>(*this);
ar & c;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
private:
double c;
};
}
后来,通过在DLL中添加导出的读写函数解决了这个问题:
#include "boost/serialization/split_member.hpp"
#include <boost/serialization/access.hpp>
#include <boost/serialization/export.hpp>
#include "boost/archive/binary_iarchive.hpp"
#include "boost/archive/binary_oarchive.hpp"
namespace CY
{
class KHC_OBJECT_EXPORTS_API CBaseA
{
public:
CBaseA();
virtual ~CBaseA();
virtual void DoNothing() {}
private:
friend class ::boost::serialization::access;
template<class Archive>
void save(Archive & ar, const unsigned int version) const {
ar & a;
}
template<class Archive>
void load(Archive & ar, const unsigned int version) {
ar & a;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
private:
double a;
};
void KHC_OBJECT_EXPORTS_API Save(const char *path, CBaseA *p);
void KHC_OBJECT_EXPORTS_API Load(const char *path, CBaseA *&p);
}
在main函数中调用Save和Load函数,都能正常读写序列化文件。