Sto usando il seguente modello per implementare la serializzazione:
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);
}
Ecco T
il tipo che si desidera serializzare Mode
è un tipo fittizio per distinguere tra diversi tipi di serializzazione, ad es. lo stesso numero intero può essere serializzato come little endian, big endian, varint, ecc.
Per impostazione predefinita, Serializer
delega l'attività all'oggetto in fase di serializzazione. Per i tipi predefiniti, è necessario creare una specializzazione modello di Serializer
.
Sono inoltre disponibili modelli di funzioni utili.
Ad esempio serializzazione little endian di numeri interi senza segno:
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++;
}
}
};
Quindi serializzare:
std::vector<char> serialized;
uint32_t val = 42;
serialize<LittleEndianMode>(val, std::back_inserter(serialized));
Per deserializzare:
uint32_t val;
deserialize(val, serialized.begin(), serialized.end());
A causa della logica astratta dell'iteratore, dovrebbe funzionare con qualsiasi iteratore (es. Stream iteratori), puntatore, ecc.