基本クラスポインタを介して派生クラスをシリアル化するときに、ブーストシリアル化に問題があります。システムで受信されているオブジェクトをシリアル化するシステムが必要なので、時間をかけてシリアル化する必要があります。boost::archive::binary_oarchive
必要に応じてオブジェクトを開いてシリアル化できるため、これは実際には問題ではありません。Boostがメモリアドレスによるオブジェクトトラッキングを実行していることにすぐに気付きました。最初の問題は、同じメモリアドレスを共有する時間内の異なるオブジェクトが同じオブジェクトとして保存されることでした。これは、必要な派生クラスで次のマクロを使用することで修正できます。
BOOST_CLASS_TRACKING(className, boost::serialization::track_never)
これは正常に機能しますが、基本クラスが抽象でない場合、基本クラスは適切にシリアル化されません。次の例では、基本クラスのシリアル化メソッドは、最初のオブジェクトで1回だけ呼び出されます。以下では、オブジェクトのタイプは異なりますが、boostはこのオブジェクトが以前にシリアル化されていることを前提としています。
#include <iostream>
#include <fstream>
#include <boost/serialization/export.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/archive/archive_exception.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
using namespace std;
class AClass{
public:
AClass(){}
virtual ~AClass(){}
private:
double a;
double b;
//virtual void virtualMethod() = 0;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & a;
ar & b;
cout << "A" << endl;
}
};
//BOOST_SERIALIZATION_ASSUME_ABSTRACT(Aclass)
//BOOST_CLASS_TRACKING(AClass, boost::serialization::track_never)
class BClass : public AClass{
public:
BClass(){}
virtual ~BClass(){}
private:
double c;
double d;
virtual void virtualMethod(){};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & boost::serialization::base_object<AClass>(*this);
ar & c;
ar & d;
cout << "B" << endl;
}
};
// define export to be able to serialize through base class pointer
BOOST_CLASS_EXPORT(BClass)
BOOST_CLASS_TRACKING(BClass, boost::serialization::track_never)
class CClass : public AClass{
public:
CClass(){}
virtual ~CClass(){}
private:
double c;
double d;
virtual void virtualMethod(){};
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & boost::serialization::base_object<AClass>(*this);
ar & c;
ar & d;
cout << "C" << endl;
}
};
// define export to be able to serialize through base class pointer
BOOST_CLASS_EXPORT(CClass)
BOOST_CLASS_TRACKING(CClass, boost::serialization::track_never)
int main() {
cout << "Serializing...." << endl;
{
ofstream ofs("serialization.dat");
boost::archive::binary_oarchive oa(ofs);
for(int i=0;i<5;i++)
{
AClass* baseClassPointer = new BClass();
// serialize object through base pointer
oa << baseClassPointer;
// free the pointer so next allocation can reuse memory address
delete baseClassPointer;
}
for(int i=0;i<5;i++)
{
AClass* baseClassPointer = new CClass();
// serialize object through base pointer
oa << baseClassPointer;
// free the pointer so next allocation can reuse memory address
delete baseClassPointer;
}
}
getchar();
cout << "Deserializing..." << endl;
{
ifstream ifs("serialization.dat");
boost::archive::binary_iarchive ia(ifs);
try{
while(true){
AClass* a;
ia >> a;
delete a;
}
}catch(boost::archive::archive_exception const& e)
{
}
}
return 0;
}
このコードを実行すると、結果は次のようになります。
Serializing....
A
B
B
B
B
B
C
C
C
C
C
Deserializing...
A
B
B
B
B
B
C
C
C
C
C
したがって、派生クラスには明示的にtrack_neverフラグがありますが、基本クラスは1回だけシリアル化されます。この動作を修正するには、2つの異なる回避策があります。1つ目は、純粋仮想メソッドを使用して基本クラスを抽象化し、マクロを呼び出すことBOOST_SERIALIZATION_ASSUME_ABSTRACT(Aclass)
です。2つ目は、基本クラスにもtrack_neverフラグを配置することです(コードでコメント化)。
これらのソリューションはいずれも私の要件を満たしていません。将来、システム状態の時間的なシリアル化を実行したいので、Aを拡張する特定のDClass(BまたはCではない)の追跡機能が必要になります。また、AClassは抽象的であってはなりません。
ヒントはありますか?基本クラス(派生クラスですでに無効になっている)の追跡機能を回避して、基本クラスのシリアル化メソッドを明示的に呼び出す方法はありますか?