J'utilise le modèle suivant pour implémenter la sérialisation:
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);
}
Voici T
le type que vous souhaitez sérialiser Mode
est un type factice pour différencier les différents types de sérialisation, par exemple. le même entier peut être sérialisé en tant que little endian, big endian, varint, etc.
Par défaut, Serializer
délègue la tâche à l'objet en cours de sérialisation. Pour les types intégrés, vous devez créer une spécialisation de modèle du Serializer
.
Des modèles de fonctions pratiques sont également fournis.
Par exemple, la sérialisation little endian d'entiers non signés:
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++;
}
}
};
Puis pour sérialiser:
std::vector<char> serialized;
uint32_t val = 42;
serialize<LittleEndianMode>(val, std::back_inserter(serialized));
Pour désérialiser:
uint32_t val;
deserialize(val, serialized.begin(), serialized.end());
En raison de la logique de l'itérateur abstrait, il devrait fonctionner avec n'importe quel itérateur (par exemple, les itérateurs de flux), pointeur, etc.