Максимум, которого можно добиться, дабы облегчить себе жизнь, это возможность писать код сериализации той или иной структуры только один раз (т.е. фактически перечислив все поля), а не два раза под запись в поток и чтение из него. Трюк этот известный, используемый, в том числе, и в библиотеке сериализации в boost -- метод сериализации получает поток неизвестного типа, задаваемого через шаблон, и пишет в него все свои поля через оператор "<<". Cоответственно есть два типа потока -- in и out, у которых перегружен оператор "<<" под то действие, которое надо выполнить. Гениально и просто, и, в свое время, некий подобный велосипед для передачи данных по сети сгородил и я:
class PacketRead { public: // ... PacketRead& operator<<(std::string &v) { v = m_pack[m_currIndx++].AsString(); return *this; } PacketRead& operator<<(int32 &v) { v = m_pack[m_currIndx++].AsInt32(); return *this; } }; class PacketWrite { public: // ... PacketWrite& operator<<(const std::string &v) { m_pack.WriteString(v); return *this; } PacketWrite& operator<<(const int32 &v) { m_pack.WriteInt32(v); return *this; } }
Используются такие потоки примерно так:
struct CofidecState { enum CsMode { mOff, mTdmToHandset, mTdmToLoud, }; CsMode Mode; // in percent 0 .. 100 int HandsetMicLevel; int HandsetSpeakerLevel; int HandsfreeMicLevel; int HandsfreeSpeakerLevel; CofidecState() { Mode = mOff; HandsetMicLevel = 0; HandsetSpeakerLevel = 0; HandsfreeMicLevel = 0; HandsfreeSpeakerLevel = 0; } template<class Data, class TStream> static void Serialize(Data &data, TStream &s) { s << data.Mode << data.HandsetMicLevel << data.HandsetSpeakerLevel << data.HandsfreeMicLevel << data.HandsfreeSpeakerLevel; } };
Метод Serialize сделан статическим, потому что для обычного метода класса не было бы возможности сделать корректную константность в случае записи в поток. В этом же случае, одна из подстановок типа Data происходит как "const CofidecState &data".
Одни грабли -- обратите внимание на поле Mode, описываемое через enum тип.
Как поддержать запись такого типа потоками?
У меня получилось так:
class PacketRead { // ... template<class TEnum> PacketRead& operator<<(TEnum &v) { BOOST_STATIC_ASSERT(boost::is_enum<TEnum>::value); BOOST_STATIC_ASSERT(sizeof(TEnum) == sizeof(int32)); v = (TEnum)( m_pack[m_currIndx++].AsInt32() ); return *this; } } class PacketWrite { // ... template<class TEnum> PacketWrite& operator<<(const TEnum &v) { BOOST_STATIC_ASSERT(boost::is_enum<TEnum>::value); BOOST_STATIC_ASSERT(sizeof(TEnum) == sizeof(int32)); m_pack.WriteInt32(v); return *this; } }
No comments:
Post a Comment