Как сериализовать объект в C++?
У меня есть небольшая иерархия объектов, которые мне нужно сериализовать и передать через соединение сокета. Мне нужно как сериализовать объект, так и десериализовать его в зависимости от типа. Есть ли простой способ сделать это на C++ (как есть в Java)?
существуют ли онлайн-примеры кода сериализации C++ или учебные пособия?
EDIT: чтобы быть ясным, я ищу методы преобразования объекта в массив байтов, а затем обратно в объект. Я смогите отрегулировать передачу гнезда.
3 ответов
говоря о сериализации повысить API сериализации приходит мне на ум. Что касается передачи сериализованных данных по сети, я бы использовал сокеты Berkeley или библиотека asio.
Edit:
Если вы хотите сериализовать свои объекты в массив байтов, вы можете использовать boost serializer следующим образом (взято с сайта учебника):
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
class gps_position
{
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & degrees;
ar & minutes;
ar & seconds;
}
int degrees;
int minutes;
float seconds;
public:
gps_position(){};
gps_position(int d, int m, float s) :
degrees(d), minutes(m), seconds(s)
{}
};
фактическая сериализация тогда довольно легко:
#include <fstream>
std::ofstream ofs("filename.dat", std::ios::binary);
// create class instance
const gps_position g(35, 59, 24.567f);
// save data to archive
{
boost::archive::binary_oarchive oa(ofs);
// write class instance to archive
oa << g;
// archive and stream closed when destructors are called
}
десериализация работает аналогичным образом.
есть также механизмы, которые позволяют обрабатывать сериализацию указателей (сложные структуры данных, такие как tress и т. д., не проблема), производные классы, и вы можете выбирать между двоичной и текстовой сериализацией. Кроме того, все контейнеры STL поддерживаются из коробки.
в некоторых случаях, при работе с простыми типами, вы можете сделать:
object o;
socket.write(&o, sizeof(o));
Это нормально, как доказательство концепции или первый проект, так что другие члены вашей команды могут работать над другими деталями.
но рано или поздно, обычно рано, это будет больно!
вы сталкиваетесь с проблемами с:
- таблицы виртуальных указателей будут повреждены.
- указатели (на данные / члены / функции) будут испорченный.
- различия в прокладке / выравнивании на разных машинах.
- проблемы с порядком байтов Big/Little-Endian.
- вариации в реализации float / double.
(плюс вам нужно знать, что вы распаковываете на принимающей стороне.)
вы можете улучшить это, разработав свои собственные методы маршалинга/unmarshalling для каждого класса. (Идеально виртуальные, поэтому они могут быть расширены в подклассах.) Несколько простые макросы позволят вам писать очень быстро различные базовые типы в большом/прямой-нейтральной заказа.
но такая ворчливая работа намного лучше и легче обрабатывается через библиотека сериализации boost.
сериализация означает превращение объекта в двоичные данные. В то время как десериализация означает воссоздание объекта из данных.
при сериализации вы нажимаете байты в uint8_t
вектор.
Когда десериализация Вы читаете байты от uint8_t
вектор.
есть, конечно, шаблоны, которые вы можете использовать при сериализации материала.
каждый сериализуемый класс должен иметь serialize(std::vector<uint8_t> &binaryData)
или аналогичная signatured функция, которая будет писать свой двоичный файл представительство в векторе. Тогда эта функция может передать этот вектор вниз к сериализации функции-члена, чтобы и они могли оставить свои вещи в него.
поскольку представление данных может быть различным на разных архитектурах. Вам нужно найти схему представления данных.
давайте начнем с основ:
сериализация данных integer
просто записать байт в маленькой прямом порядке. Или использовать варинт представление, если размер имеет значение.
сериализация в маленькой прямом порядке:
data.push_back(integer32 & 0xFF);
data.push_back((integer32 >> 8) & 0xFF);
data.push_back((integer32 >> 16) & 0xFF);
data.push_back((integer32 >> 24) & 0xFF);
десериализация из little endian order:
integer32 = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
сериализация данных с плавающей запятой
насколько я знаю, IEEE 754 имеет монополию здесь. Я не знаю никакой основной архитектуры, которая использовала бы что-то еще для поплавков. Единственное что может отличаться-это порядок байт. Некоторые архитектуры используют little endian, другие - big endian byte порядок. Это означает, что вам нужно быть осторожным, какой заказ вам громче байтов на принимающей стороне. Еще одним различием можно регулировать Донормила и бесконечность и NaN значения. Но пока вы избегаете этих значений, вы должны быть в порядке.
сериализация:
uint8_t mem[8];
memcpy(mem, doubleValue, 8);
data.push_back(mem[0]);
data.push_back(mem[1]);
...
десериализация делает это назад. Следите за порядком байтов вашей архитектуры!
сериализации строки
сначала вам нужно договориться о кодировке. UTF-8 распространен. Затем сохраните его как длину с префиксом: сначала вы сохраняете длину строки с помощью метода, упомянутого выше, а затем записываете строку байт за байтом.
сериализовать массивы.
они такие же, как струны. Сначала сериализуется целое число, представляющее размер массива, а затем сериализуется каждый объект в нем.
сериализовать все объекты
как я уже говорил раньше, они должны иметь serialize
метод, который добавляет содержимое в вектор.
Чтобы восстановить объект, он должен иметь конструктор, который принимает поток байтов. Это может быть istream
но в простейшем случае это может быть просто ссылка