Расширение ускоренной сериализации

Я пытаюсь настроить многоцелевую сериализацию для сетевой видеоигры для мобильных устройств. Поскольку он подключен к сети, во время первоначального подключения мне нужно сериализовать все данные для состояния игры, однако, когда игра уже идет, мне нужно будет только сериализовать определенные изменения. Методы сохранения и загрузки, которые являются частью библиотеки расширенной сериализации, имеют только номер версии в качестве параметра. То, что я хотел бы иметь, это иметь больше параметров, чтобы я мог изменять условия для того, что сохраняется и загружается, основываясь не только на номере версии.

Ускоренные документы по сериализации здесь, для справки.

Вот что такое простое повышение сериализации save метод в настоящее время выглядит так:

template<class Archive>
void save(Archive& ar, const unsigned int version) const
{
// Serialize stuff here
}

Вот что я хотел бы сделать:

template<class Archive>
void save(Archive& ar, const unsigned int version, const unsigned int state_flags) const
{
if (state_flags & INITIAL_SERIALIZATION)
{
// Serialize only data needed for an initial serialization
}

// Other serialization
}

Я сомневаюсь, что могу заставить библиотеку boost вызывать мой метод сериализации, который мне нужен, потому что в нем перегружены операторы, созданные для вызова оператора с определенной сигнатурой в первом примере выше. Я воображаю, называя свою собственную версию save из звонка save показано в первом примере, и, возможно, захватить state_flags из отдельного места. У кого-нибудь есть идеи о том, как это можно сделать чисто, или есть хорошие альтернативы?

РЕДАКТИРОВАТЬ:
Я столкнулся с другой проблемой. Мне нужно сериализовать объекты, которые не обязательно являются членами класса, но в документации не упоминается какая-либо поддержка для этого.

Вот простой пример:

class Foo
{
private:
SomeClass m_object;

template<class Archive>
void save(Archive& ar, const unsigned int version) const
{
Bar* pBar = m_object->getComponent<Bar>();
ar & pBar;   // <--- But pBar isn't a member of Bar, it's part of SomeClass.
}
};

Я бы просто сериализовал SomeClass и пусть эта струйка до Bar, но в данном случае это класс, являющийся частью сторонней библиотеки / движка, а не то, что я могу изменить. Позволит ли сериализация Boost мне сериализовать и десериализовать таким образом?

5

Решение

РЕДАКТИРОВАТЬ: новый ответ добавлен ниже для решения актуальной проблемы.

Ваш вопрос подразумевает, что вы повторно десериализуете один и тот же объект. Это косвенно, чисто это или нет. Если, например, у вас есть шахматная доска, вы бы хотели
синхронизировать начальную позицию фигур (чтобы продолжить с последней сохраненной игры). Чтобы сообщать ходы во время игры, лучше отправлять отдельные ходы.
как отдельные объекты (которые затем применяются к объекту платы после получения) вместо передачи всего объекта платы, который будет передавать только то, что изменилось, если оно уже «инициализировано».
Таким образом, вы можете сначала проверить ввод и игнорировать неверные ходы. Во всяком случае, я просто хотел упомянуть об этом, давайте двигаться дальше.

Если у вас есть объект, который может быть синхронизирован несколько раз, с данными о членах, которые нужно передать только один раз, позвольте объекту решить, «инициализирован» он или нет.
(и, следовательно, если ему нужно передать все или только подмножество), используя флаг (который не сериализован).

Затем вы можете проверить флаг в коде сериализации объекта, как и в опубликованном вами коде (за исключением того, что флаг не является параметром для метода сериализации,
но переменная-член объекта, который вы де / сериализуете). Если флаг установлен, де / сериализуйте все и сбросьте флаг. И клиент, и сервер должны иметь одинаковое состояние флага, иначе сериализация прерывается.

В качестве альтернативы вы могли бы сначала сериализовать флаг, чтобы сообщить получателю, как должна выполняться десериализация (например, один бит для каждой группы данных члена).

Имейте в виду, что десериализация должна соответствовать сериализации; Вы должны извлечь те же объекты в том же порядке, в котором они были сериализованы.

Однако вы можете сериализовать полиморфные классы, если они сериализуются на том же уровне в иерархии классов, что и десериализованные.
(если есть сомнения, приведите к базовому указателю при отправке и десериализации также через базовый указатель).

Что касается вашего второго вопроса, то, что вы ищете ненавязчивая сериализация.
Ненавязчивая сериализация вызывает автономные функции и передает сериализуемый объект в качестве параметра (так сериализуются 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();
}

Цель этого примера — просто проиллюстрировать принцип. Это слишком просто для производственного кода.

3

Другие решения

Я придумал решение этой проблемы, и, хотя я и не был идеален, я подумал, что в любом случае стоит опубликовать. По сути, я настроил одноэлементный класс для управления отправкой всех запросов сериализации, и этот класс будет отслеживать последние битовые флаги, которые использовались для этого запроса. Поэтому во время сериализации или десериализации эти методы могут запрашивать эти флаги. Это позволило мне иметь Boost’s save а также load методы вызывают более надежный набор методов, которые могут использовать эти флаги для выборочной сериализации только определенных членов.

// Boost's `save` method, which must have this exact signature
template<class Archive>
void save(Archive& ar, const unsigned int version) const
{
const unsigned int flags = SerializationManager::getFlags(); // SerializationManager is a singleton.
saveData(ar, version, flags);
}

// Your own custom method, which can have whichever parameters you need
template<class Archive>
void saveData(Archive& ar, const unsigned int version, const unsigned int state_flags) const
{
if (state_flags & INITIAL_SERIALIZATION)
{
// Serialize only data needed for an initial serialization
}

// Other serialization
}
0

Вот более простой способ:

// Boost's `save` method, which must have this exact signature
template<class Archive>
void save(Archive& ar, const unsigned int version) const
{
const unsigned int flags = SerializationManager::getFlags(); //         SerializationManager is a singleton.
ar << flags;
if(flags && INITIAL_SERIALIZATION){
// Serialize only data needed for an initial serialization
}
// Other serialization
}
template<class Archive>
void load(Archive& ar, const unsigned int version) const
{
const unsigned int flags = SerializationManager::getFlags(); //         SerializationManager is a singleton.
unsigned int flags;
ar >> flags;
if(flags && INITIAL_SERIALIZATION){
// Serialize only data needed for an initial serialization
}
// Other serialization
}
0