Estou usando o seguinte modelo para implementar a serialização:
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);
}
Aqui T
está o tipo que você deseja serializar Mode
é um tipo fictício para diferenciar entre diferentes tipos de serialização, por exemplo. o mesmo número inteiro pode ser serializado como little endian, big endian, varint etc.
Por padrão, Serializer
delega a tarefa ao objeto que está sendo serializado. Para tipos incorporados, você deve fazer uma especialização de modelo do Serializer
.
Modelos de função de conveniência também são fornecidos.
Por exemplo, pequena serialização endiana de números inteiros não assinados:
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++;
}
}
};
Em seguida, para serializar:
std::vector<char> serialized;
uint32_t val = 42;
serialize<LittleEndianMode>(val, std::back_inserter(serialized));
Para desserializar:
uint32_t val;
deserialize(val, serialized.begin(), serialized.end());
Devido à lógica abstrata do iterador, ele deve funcionar com qualquer iterador (por exemplo, iteradores de fluxo), ponteiro, etc.