Do implementacji serializacji używam następującego szablonu:
template <class T, class Mode = void> struct Serializer
{
template <class OutputCharIterator>
static void serializeImpl(const T &object, OutputCharIterator &&it)
{
object.template serializeThis<Mode>(it);
}
template <class InputCharIterator>
static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
{
return T::template deserializeFrom<Mode>(it, end);
}
};
template <class Mode = void, class T, class OutputCharIterator>
void serialize(const T &object, OutputCharIterator &&it)
{
Serializer<T, Mode>::serializeImpl(object, it);
}
template <class T, class Mode = void, class InputCharIterator>
T deserialize(InputCharIterator &&it, InputCharIterator &&end)
{
return Serializer<T, Mode>::deserializeImpl(it, end);
}
template <class Mode = void, class T, class InputCharIterator>
void deserialize(T &result, InputCharIterator &&it, InputCharIterator &&end)
{
result = Serializer<T, Mode>::deserializeImpl(it, end);
}
Oto T
typ, który chcesz serializować, Mode
jest typem fikcyjnym do rozróżniania różnych rodzajów serializacji, np. ta sama liczba całkowita może być serializowana jako little endian, big endian, varint itp.
Domyślnie Serializer
deleguje zadanie do serializowanego obiektu. W przypadku typów wbudowanych należy utworzyć specjalizację szablonu Serializer
.
Dostępne są również wygodne szablony funkcji.
Na przykład serializacja little endian liczb całkowitych bez znaku:
struct LittleEndianMode
{
};
template <class T>
struct Serializer<
T, std::enable_if_t<std::is_unsigned<T>::value, LittleEndianMode>>
{
template <class InputCharIterator>
static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
{
T res = 0;
for (size_t i = 0; i < sizeof(T); i++)
{
if (it == end) break;
res |= static_cast<T>(*it) << (CHAR_BIT * i);
it++;
}
return res;
}
template <class OutputCharIterator>
static void serializeImpl(T number, OutputCharIterator &&it)
{
for (size_t i = 0; i < sizeof(T); i++)
{
*it = (number >> (CHAR_BIT * i)) & 0xFF;
it++;
}
}
};
Następnie do serializacji:
std::vector<char> serialized;
uint32_t val = 42;
serialize<LittleEndianMode>(val, std::back_inserter(serialized));
Aby deserializować:
uint32_t val;
deserialize(val, serialized.begin(), serialized.end());
Ze względu na abstrakcyjną logikę iteratora powinien działać z dowolnym iteratorem (np. Iteratorami strumienia), wskaźnikiem itp.