Я використовую такий шаблон для здійснення серіалізації:
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);
}
Ось T
тип, який ви хочете серіалізувати, Mode
- це фіктивний тип для розмежування різних видів серіалізації, наприклад. те саме ціле число можна серіалізувати як маленький ендіан, великий ендіан, варінт тощо.
За замовчуванням Serializer
делегує завдання об'єкту, що серіалізується. Для вбудованих типів слід зробити спеціалізацію шаблону Serializer
.
Також надаються шаблони функцій зручності.
Наприклад, мала ендіанська серіалізація цілих чисел, які не підписуються:
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++;
}
}
};
Потім для серіалізації:
std::vector<char> serialized;
uint32_t val = 42;
serialize<LittleEndianMode>(val, std::back_inserter(serialized));
Десеріалізувати:
uint32_t val;
deserialize(val, serialized.begin(), serialized.end());
Завдяки абстрактній логіці ітератора він повинен працювати з будь-яким ітератором (наприклад, потоком ітераторів), покажчиком тощо.