EDYCJA: nowa odpowiedź została dodana poniżej w celu rozwiązania faktycznego problemu.
Twoje pytanie sugeruje, że deserializujesz wielokrotnie ten sam obiekt. Jest to poszlakowe, jeśli jest czyste, czy nie. Jeśli na przykład masz szachownicę, zechcesz zsynchronizować początkową pozycję figur (aby kontynuować od ostatniej zapisanej gry). Aby komunikować ruchy podczas grania, lepszym pomysłem jest wysłanie pojedynczych ruchów jako oddzielnych obiektów (które następnie zostaną zastosowane do obiektu płyty po otrzymaniu) zamiast transmitowania całego obiektu płyty, który przenosi tylko to, co zostało zmienione, jeśli jest już "zainicjalizowane". W ten sposób można najpierw sprawdzić poprawność danych wejściowych i zignorować nieprawidłowe ruchy. W każdym razie, chciałem o tym wspomnieć, przejdźmy dalej.
Jeśli istnieje obiekt, który może być synchronizowany wiele razy, z danymi elementu, które należy przekazać tylko raz, niech obiekt zdecyduje, czy jest "zainicjowany" czy nie (i w konsekwencji, jeśli musi przekazać wszystko lub tylko zestaw podrzędny) za pomocą flagi (która nie jest serializowana).
Następnie możesz sprawdzić flagę w kodzie serializacji obiektu, tak jak w kodzie, który wysłałeś (z wyjątkiem tego, że flaga nie jest parametrem metody serializacji, , ale zmienną składową obiektu, który masz de/serializowanie).Jeśli flaga jest ustawiona, usuń/serializuj wszystko i zresetuj flagę. Zarówno klient, jak i serwer muszą mieć ten sam stan flagi lub serializacja.
Alternatywnie możesz najpierw serializować flagę, aby poinformować odbiorcę, jak należy przeprowadzić deserializację (na przykład jeden bit dla każdej grupy danych członka).
Należy pamiętać, że deserializacja musi być zgodna z serializacją; Musisz wyodrębnić te same obiekty w tej samej kolejności, w jakiej były serializowane.
Można jednak szeregować klasy polimorficzne, biorąc pod uwagę, że są serializowane na tym samym poziomie w hierarchii klas, ponieważ są deserializowane (w razie wątpliwości, rzutuj na wskaźnik bazowy podczas wysyłania i deserializacji za pomocą wskaźnika bazowego, jak również).
Jeśli chodzi o drugie pytanie, szukamy non-intrusive serialization. Nieinwazyjna serializacja wywołuje funkcje wolnostojące i przekazuje obiekt do serializacji jako parametr (w ten sposób std :: vector i boost :: shared_ptr są serializowane). Możesz użyć BOOST_SERIALIZATION_SPLIT_FREE
, aby podzielić wolnostojącą funkcję serialize()
na save()
i load()
. Dla intruzywnej serializacji jest to BOOST_SERIALIZATION_SPLIT_MEMBER
.
Aby napisać uogólnioną funkcję de/serializacji (który przenosić obiekty przez sieć na przykład) można użyć szablonów:
template<typename T>
void transmit(const T& data) {
// ...
archive << data
socket << archive_stream;
}
Ograniczenie tej metody jest to, że odbiornik musi wiedzieć, jaki rodzaj obiektu został wysłany. Jeśli chcesz wysłać przypadkowych obiektów, ich polimorficzny:
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));
}
UPDATE: jeśli trzeba kontrolować w jaki sposób (niestandardowe) metody serializacji/funkcje zachowują się, w oparciu o stan nieznanych typów będąc serializowane, możesz zaimplementować własną klasę archiwum, która przechowuje stan. Funkcje serializacji mogą następnie zapytać o stan i odpowiednio do tego działać.
Ten stan (lub odpowiednia podstawa) musi być również serializowany, aby wskazać sposób deserializacji danych. Na przykład, to "inne zachowanie" funkcji serializacji może być pewnego rodzaju kompresją, a stan jest rodzajem stosowanej kompresji.
Oto minimalny przykład niestandardowego archiwum wyjściowego. Aby uzyskać więcej informacji, przeczytaj Derivation from an Existing Archive i przeglądaj źródła boost.
Biorąc pod uwagę klasę nie można modyfikować:
struct Foo {
Foo() : i(42), s("foo") {}
int i;
std::string s;
};
Ty chcesz serializacji i
i/lub s
na podstawie stanu nieznanego do klasy. Możesz utworzyć wrapper, aby przekształcić go do postaci szeregowej i dodać stan, ale to nie zadziała, jeśli obiekt znajduje się wewnątrz wektora (lub innej klasy).
To może być łatwiejsze archiwum świadomi stanu Zamiast:
#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>
Teraz państwowej ustawić i zapytania:
enum state { FULL=0x10, PARTIAL=0x20 };
i sposób ustawić stan (jest to tylko bardzo prosty przykład):
oarchive& operator<<(oarchive& ar, state mask) {
ar.set_mask(ar.get_mask()|mask);
return ar;
}
Wreszcie (nieinwazyjne) funkcja serializacji:
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)
A może to zostać wykorzystane w następujący sposób:
int main() {
std::stringstream strm;
oarchive ar(strm);
Foo f;
ar << PARTIAL << f << FULL << f;
std::cout << strm.str();
}
Celem tego przykładu jest tylko zilustrować zasadę. Jest zbyt podstawowy dla kodu produkcyjnego.
Problem mam z Boost jest, że muszę serializacji członków klasy, co chcę robić w niektórych przypadkach jest serializacji tylko części klasy, części, które mogą nie być zmiennymi składowymi, ale raczej dane zbudowane z części zmiennych składowych. Nie będę w pełni serializować wszystkich danych za każdym razem, będę serializować tylko dane potrzebne do ustalenia, co się zmieniło. Tylko początkowa serializacja będzie zawierać wszystkie dane. –
W oparciu o moje ogólne potrzeby zdecydowałem jednak, że nie będę korzystać z Boostowania serializacji. Przeważnie chciałem mieć bibliotekę, która dzieliłaby typowe typy danych na/z binarnych, ale nie było to dokładnie to, czego szukałem. Prawdopodobnie po prostu napiszę własne metody konwersji typów do/z binarnych i użyję oddzielnej biblioteki do kompresowania/dekompresji bitstreamów dla mnie. Twoja odpowiedź zbliżyła się do tego, co było mi potrzebne na podstawie mojego pytania, więc zaznaczę to jako odpowiedź na moje pytanie. Dzięki. –
@NicFoster i miałem zamiar usunąć odpowiedź, ponieważ źle zrozumiałem twoje pytanie. nie krępuj się i nie odzywaj, dopóki ja (lub ktoś inny) nie zaproponuje odpowiedniego rozwiązania. Chcesz serializować klasę, której nie możesz zmodyfikować w środku (na przykład) wektora i chcesz kontrolować sposób przekształcania jej do postaci szeregowej bez powrotu do zmiennej globalnej. –