РЕДАКТИРАНЕ: по-долу е добавен нов отговор за справяне с действителния проблем.
Вашият въпрос предполага, че десериализирате към един и същ обект многократно. Косвенно е дали това е чисто или не. Ако, например, имате дъска за шах, бихте искали да синхронизирате първоначалната позиция на фигурите (за да продължите от последната запазена игра). За да съобщите ходовете, докато се играе играта, може да е по-добра идея да изпратите отделните ходове като отделни обекти (които след това се прилагат към обекта на дъската, след като бъдат получени), вместо да предавате целия обект на дъската, който ще прехвърли само това, което е променено ако вече е "инициализирано". По този начин можете първо да потвърдите въвеждането и да игнорирате невалидни ходове. Както и да е, просто исках да спомена това, нека продължим.
Ако имате обект, който може да бъде синхронизиран многократно, с данни за член, които трябва да бъдат прехвърлени само веднъж, оставете обекта да реши дали е „инициализиран“ или не (и следователно, дали трябва да предаде всичко или само поднабор) чрез използване на флаг (който не е сериализиран).
След това можете да проверите флага в кода за сериализация на обекта, точно както в кода, който сте публикували (с изключение на това, че флагът не е параметър на метода за сериализация, а членска променлива на обекта, който де/сериализирате). Ако флагът е зададен, де/сериализирайте всичко и нулирайте флага. Както клиентът, така и сървърът трябва да имат едно и също състояние на флага или сериализацията се прекъсва.
Като алтернатива можете първо да сериализирате флага, за да кажете на приемника как трябва да се извърши десериализацията (например един бит за всяка членска група данни).
Имайте предвид, че десериализацията трябва да съответства на сериализацията; Трябва да извлечете същите обекти в същия ред, в който са били сериализирани.
Можете обаче да сериализирате полиморфни класове, като се има предвид, че те са сериализирани през същото ниво в йерархията на класовете, както са десериализирани (когато се съмнявате, прехвърлете към базовия указател при изпращане и десериализирайте също чрез базовия указател).
Относно втория ви въпрос, това, което търсите, е ненатрапчива сериализация. Ненатрапчивата сериализация извиква самостоятелни функции и предава обекта, който трябва да бъде сериализиран като параметър (по този начин се сериализират std::vector и boost::shared_ptr). Можете да използвате BOOST_SERIALIZATION_SPLIT_FREE
, за да разделите отделната функция serialize()
на save()
и load()
. За натрапчива сериализация е BOOST_SERIALIZATION_SPLIT_MEMBER
.
За да напишете обобщена функция за де/сериализация (която например предава обекти през мрежата), можете да използвате шаблони:
template<typename T>
void transmit( const T& data ) {
// ...
archive << data
socket << archive_stream;
}
Ограничението при този метод е, че получателят трябва да знае какъв вид обект е изпратен. Ако искате да изпратите произволни обекти, направете ги полиморфни:
IData* data = 0;
archive >> data;
switch( data->type() ) {
case TYPE_INIT:
return dispatch( static_cast<Board*>(data) );
case TYPE_MOVE:
return dispatch( static_cast<Move*>(data) );
case TYPE_CHAT:
return dispatch( static_cast<ChatMsg*>(data) );
}
АКТУАЛИЗАЦИЯ: Ако трябва да контролирате как се държат вашите (персонализирани) методи/функции за сериализиране въз основа на състояние, непознато за типовете, които се сериализират, можете да приложите свой собствен архивен клас, който съхранява състоянието. След това функциите за сериализиране могат да направят заявка за състоянието и да действат съответно.
Това състояние (или подходящо заместване) трябва също да бъде сериализирано, за да посочи как данните трябва да бъдат десериализирани. Например, това „различно поведение“ на функциите за сериализация може да бъде някакъв вид компресия, а състоянието е вида на използваната компресия.
Ето минимален пример за персонализиран изходен архив. За повече информация можете да прочетете Извличане от съществуващ архив и разровете източниците на усилване.
Като се има предвид клас, който не можете да променяте:
struct Foo {
Foo() : i(42), s("foo") {}
int i;
std::string s;
};
Искате да сериализирате i
и/или s
въз основа на условие, неизвестно на класа. Можете да създадете обвивка, за да го сериализирате и да добавите състоянието, но това няма да работи, ако обектът е вътре във вектор (или друг клас за този въпрос).
Вместо това може да е по-лесно да информирате архива за състоянието:
#include <boost/archive/text_oarchive.hpp>
// using struct to omit a bunch of friend declarations
struct oarchive : boost::archive::text_oarchive_impl<oarchive>
{
oarchive(std::ostream& os, unsigned flags=0)
: boost::archive::text_oarchive_impl<oarchive>(os,flags),mask(0){}
// forward to base class
template<class T> void save( T& t ) {
boost::archive::text_oarchive_impl<oarchive>::save(t);
}
// this is the 'state' that can be set on the archive
// and queried by the serialization functions
unsigned get_mask() const { return mask; }
void set_mask(unsigned m) { mask = m; }
void clear_mask() { mask = 0; }
private:
unsigned mask;
};
// explicit instantiation of class templates involved
namespace boost { namespace archive {
template class basic_text_oarchive<oarchive>;
template class text_oarchive_impl<oarchive>;
template class detail::archive_serializer_map<oarchive>;
} }
// template implementations (should go to the .cpp)
#include <boost/archive/impl/basic_text_oarchive.ipp>
#include <boost/archive/impl/text_oarchive_impl.ipp>
#include <boost/archive/impl/archive_serializer_map.ipp>
Сега състоянието за задаване и заявка:
enum state { FULL=0x10, PARTIAL=0x20 };
И метод за задаване на състоянието (това е само много основен пример):
oarchive& operator<<(oarchive& ar, state mask) {
ar.set_mask(ar.get_mask()|mask);
return ar;
}
И накрая, (ненатрапчивата) функция за сериализация:
namespace boost { namespace serialization {
template<class Archive>
void save(Archive & ar, const Foo& foo, const unsigned int version)
{
int mask = ar.get_mask(); // get state from the archive
ar << mask; // serialize the state! when deserializing,
// read the state first and extract the data accordingly
if( mask & FULL )
ar << foo.s; // only serialize s if FULL is set
ar << foo.i; // otherwise serialize i only
ar.clear_mask(); // reset the state
}
} } // boost::serialization
BOOST_SERIALIZATION_SPLIT_FREE(Foo)
И това може да се използва, както следва:
int main() {
std::stringstream strm;
oarchive ar(strm);
Foo f;
ar << PARTIAL << f << FULL << f;
std::cout << strm.str();
}
Целта на този пример е просто да илюстрира принципа. Това е твърде основно за производствен код.
person
Anonymous Coward
schedule
28.12.2012