so_4: Версия 4.4.0. Ручная сериализация полей сообщений с помощью oess_1::io
Версии SObjectizer до 4.4.0 позволяли использовать в качестве полей сообщений глобальных агентов (т.е. полей, которые будут сериализоваться через SOP) поля элементарных типов (т.к. char, int, float, double, std::string) и типов, производных от oess_1::stdsn::serializable_t (т.е. поддерживающих oess_1::stdsn сериализацию). Такое положение вещей оказалось слишком жестким при попытке использовать SObjectizer совместно с другими объектными библиотеками (например, такими как wxWidgets), в которых есть собственные классы для многих фундаментальных понятий (таких как строки). Подобные классы невозможно сделать производными от oess_1::stdsn::serializable_t и при создании сообщений глобальных агентов требовалось применять какие-то ухищрения для преобразования данных в формат, понимаемый SObjectizer-ом.
В версии 4.4.0 в SObjectizer добавлена возможность использовать в качестве полей сообщений глобальных агентов поля типов, для которых определены операторы сдвига в oess_1::io::ostream_t и из oess_1::io::istream_t (т.е. для типов, которые поддерживают ручную oess_1::io сериализацию).
Для того, чтобы включить в сообщение глобавльного агента сериализуемое вручную поле необходимо:
- определить операторы сдвигов для типа поле в/из oess_1::io::ostream_t/istream_t.
- при описании поля для SObjectizer использовать макрос SOL4_MSG_FIELD_OESS_IO_CUSTOM.
Это все. Остальную работу будет брать на себя SObjectizer.
Например, передача объекта типа wxRect через SOP может осуществляться следующим образом. Сначала определяются операторы сдвига для wxRect:
oess_1::io::ostream_t &
operator<<( oess_1::io::ostream_t & s, const wxRect & r )
{
return ( s << r.GetX() << r.GetY() << r.GetWidth() << r.GetHeight() );
}
oess_1::io::istream_t &
operator>>( oess_1::io::istream_t & s, wxRect & r )
{
int x, y, w, h;
s >> x >> y >> w >> h;
r = wxRect( x, y, w, h );
return s;
}
Затем описывается сообщение, полями которого будут объекты типа wxRect:
class a_some_global_agent_t : public so_4::rt::agent_t
{
public :
struct msg_target_area_changed
{
wxRect m_new_area;
...
};
...
};
После чего поле msg_target_area_changed::m_new_area специальным образом описывается для SObjectizer:
Ручная oess_1::io сериализация добавлена в SObjectizer из-за необходимости упростить интеграцию SObjectizer с другими объектными библиотеками. Тем не менее, при использовании ручной сериализации есть очень большая вероятность попасть в одну из следующих ловушек:
- невозможность связать по SOP приложения, которые работают с разными объектными библиотеками. Например, GUI часть приложения может использовать wxWidgets и задавать координаты областей через wxRect. Если серверная часть не использует wxWidgets, то она не сможет получать сообщения в которых есть поля типа wxRect (либо серверной части придется делать свою версию глобального агента, в которой координаты будут задаваться каким-то собственным типом, ручная сериализация которого совместима с ручной сериализацией wxRect).
- невозможность расширения сериализуемого вручную объекта. Это, вероятно, самое плохое, что есть в ручной сериализации. Если при первой реализации ручной сериализации не задуматься о том, как объект будет расширяться в будущем, то при возникновении необходимости такого расширения окажется, что старый способ сериализации будет не совместим с новым.
Подобные проблемы решаются в oess_1::stdsn сериализации, поэтому следует отдавать предпочтение именно oess_1::stdsn сериализации сложных объектов. А ручную oess_1::io сериализацию использовать только для типов, модификация которых не возможна (как в случае с классами из wxWidgets).
Примеры использования ручной сериализации можно увидеть в примерах
sample/filter/c1i.hpp,
sample/filter/c1i.cpp,
sample/filter/c2i.hpp,
sample/filter/c2i.cpp.