Ho alcuni problemi con la serializzazione boost durante la serializzazione della classe derivata tramite il puntatore della classe base. Ho bisogno di un sistema che serializzi alcuni oggetti mentre vengono ricevuti nel sistema, quindi ho bisogno di serializzare nel tempo. Questo non è davvero un problema dal momento che posso aprire un oggetto boost::archive::binary_oarchive
e serializzare quando richiesto. Rapidamente ho notato che boost stava eseguendo il tracciamento degli oggetti per indirizzo di memoria, quindi il primo problema era che diversi oggetti nel tempo che condividevano lo stesso indirizzo di memoria venivano salvati come lo stesso oggetto. Questo può essere risolto utilizzando la seguente macro nella classe derivata richiesta:Serializzazione di classe derivata senza il monitoraggio della classe in Boost (C++)
BOOST_CLASS_TRACKING(className, boost::serialization::track_never)
Questo funziona bene, ma ancora una volta, quando la classe base non è astratta, la classe di base non è serializzato correttamente. Nell'esempio seguente, il metodo di serializzazione della classe base viene chiamato una sola volta con il primo oggetto. Di seguito, boost presuppone che questo oggetto sia stato serializzato prima sebbene l'oggetto abbia un tipo differente.
#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;
}
Quando si esegue questo pezzo di codice, il risultato è il seguente:
Serializing....
A
B
B
B
B
B
C
C
C
C
C
Deserializing...
A
B
B
B
B
B
C
C
C
C
C
Quindi la classe di base è solo essere serializzato una volta, anche se la classe derivata ha esplicitamente la bandiera track_never. Esistono due soluzioni alternative per correggere questo comportamento. Il primo è quello di fare astrazione della classe base con un metodo virtuale puro e chiamando la macro BOOST_SERIALIZATION_ASSUME_ABSTRACT(Aclass)
, e la seconda è di mettere il flag track_never anche nella classe base (commentato nel codice).
Nessuna di queste soluzioni soddisfa i miei requisiti, dal momento che voglio eseguire in futuro serializzazioni puntuali dello stato del sistema, che richiederebbero funzionalità di tracciamento per un determinato DClass che estende A (non B o C), e anche l'AClass dovrebbe non essere astratto.
Eventuali suggerimenti? C'è un modo per chiamare esplicitamente il metodo di serializzazione della classe base evitando la funzione di tracciamento nella classe base (che è già stato disabilitato nella classe derivata)?
Può fare in modo che l'amplificazione presuma che A sia virtuale anche se non lo è (voglio dire, basta decommentare la riga 'BOOST_SERIALIZATION_ASSUME_ABSTRACT (Aclass)' e provare a compilare)? – Synxis