Как вы сериализуете объект на C ++?

У меня есть небольшая иерархия объектов, которые мне нужно для сериализации и передачи через соединение сокета. Мне нужно как сериализовать объект, а затем десериализировать его на основе того, какой он тип. Есть ли простой способ сделать это на C ++ (как есть в Java)?

Существуют ли какие-либо примеры кода или обучающие программы для сериализации C ++-сериализации?

EDIT: Чтобы быть ясным, я ищу методы для преобразования объекта в массив байтов, а затем обратно в объект. Я могу обрабатывать передачу сокетов.

Говоря о сериализации, мне приходит в голову API-интерфейс с расширением сериализации . Что касается передачи сериализованных данных по сети, я бы либо использовал сокеты Berkeley или библиотеку asio .

Редактировать:
Если вы хотите сериализовать свои объекты в массиве байтов, вы можете использовать сериализатор boost следующим образом (взятый с сайта учебника):

#include  #include  class gps_position { private: friend class boost::serialization::access; template 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  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 и т. Д., Без проблем), производные classы, и вы можете выбирать между двоичной и текстовой сериализацией. Кроме того, все контейнеры STL поддерживаются из коробки.

В некоторых случаях, когда вы имеете дело с простыми типами, вы можете:

 object o; socket.write(&o, sizeof(o)); 

Это нормально, как доказательство концепции или первый проект, поэтому другие члены вашей команды могут продолжать работать над другими частями.

Но рано или поздно, как правило , это будет вам больно!

У вас возникают проблемы с:

  • Таблицы виртуальных указателей будут повреждены.
  • Указатели (к данным / членам / функциям) будут повреждены.
  • Различия в заполнении / выравнивании на разных машинах.
  • Проблемы с большим / мало-Endian байтом.
  • Вариации в реализации float / double.

(Кроме того, вам нужно знать, что вы распаковываете на принимающей стороне.)

Вы можете улучшить это, разработав собственные методы маршаллинга / parsingки для каждого classа. (В идеале виртуальные, поэтому они могут быть расширены в подclassах.) Несколько простых макросов позволят вам довольно быстро выписывать различные базовые типы в порядке, отличном от большого / мало-endian-нейтрального.

Но такая грубая работа намного лучше и проще, обрабатывается через библиотеку сериализации boost .

Сериализация означает преrotation вашего объекта в двоичные данные. Хотя десериализация означает воссоздание объекта из данных.

При сериализации вы uint8_t байты в вектор uint8_t . Когда unserializing вы читаете байты из вектора uint8_t .

Конечно, шаблоны можно использовать при сериализации.

Каждый сериализуемый class должен иметь serialize(std::vector &binaryData) или аналогичную функцию подписи, которая будет записывать свое двоичное представление в предоставленный вектор. Тогда эта функция может передать этот вектор до его функций сериализации члена, чтобы они могли писать в них свои вещи.

Поскольку представление данных может быть различным на разных архитектурах. Вам нужно выяснить схему представления данных.

Начнем с основ:

Сериализация целых данных

Просто напишите байты в маленьком концевом порядке. Или используйте varint представление, если размер имеет значение.

Сериализация в порядке малых порядков:

 data.push_back(integer32 & 0xFF); data.push_back((integer32 >> 8) & 0xFF); data.push_back((integer32 >> 16) & 0xFF); data.push_back((integer32 >> 24) & 0xFF); 

Дезериализация из небольшого порядкового числа:

 integer32 = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); 

Сериализация данных с плавающей запятой

Насколько я знаю, IEEE 754 имеет monoполию здесь. Я не знаю какой-либо основной архитектуры, которая использовала бы что-то еще для float. Единственное, что может быть другим, это порядок байтов. В некоторых архитектурах используется немного endian, другие используют большой байтовый порядок байтов. Это означает, что вам нужно быть осторожным, какой порядок вам громче байт на принимающей стороне. Другим отличием может быть обработка значений denormal и бесконечности и NAN. Но пока вы избегаете этих значений, вы должны быть в порядке.

Сериализация:

 uint8_t mem[8]; memcpy(mem, doubleValue, 8); data.push_back(mem[0]); data.push_back(mem[1]); ... 

Дезициализация делает это назад. Помните порядок байтов вашей архитектуры!

Сериализация строк

Сначала вам нужно согласовать кодировку. UTF-8 является обычным явлением. Затем сохраните его как префикс длины: сначала вы сохраняете длину строки, используя метод, о котором я упоминал выше, а затем пишу строку побайтно.

Сериализация массивов.

Они такие же, как строки. Сначала вы сериализуете целое число, представляющее размер массива, затем сериализуйте каждый объект в нем.

Сериализация целых объектов

Как я уже говорил, у них должен быть метод serialize который добавляет контент в вектор. Чтобы не инициализировать объект, он должен иметь конструктор, который принимает stream байтов. Это может быть istream но в простейшем случае это может быть просто указатель uint8_t . Конструктор считывает нужные байты из streamа и настраивает поля в объекте. Если система хорошо спроектирована и сериализует поля в порядке полей объектов, вы можете просто передать stream конструкторам поля в списке инициализаторов и их десериализовать в правильном порядке.

Сериализация графиков объектов

Сначала вам нужно убедиться, что эти объекты действительно являются чем-то, что вы хотите сериализовать. Вам не нужно сериализовывать их, если экземпляры этих объектов присутствуют в пункте назначения.

Теперь вы выяснили, что вам нужно сериализовать этот объект, на который указывает указатель. Проблема указателей в том, что они действительны только в программе, которая их использует. Вы не можете сериализовать указатель, вы должны прекратить использовать их в объектах. Вместо этого создайте пулы объектов. Этот пул объектов в основном представляет собой динамический массив, который содержит «ящики». Эти поля имеют счетчик ссылок. Не нулевой счетчик ссылок указывает на живой объект, ноль указывает на пустой слот. Затем вы создаете умный указатель, похожий на shared_ptr, который не хранит указатель на объект, а индекс в массиве. Вам также необходимо согласовать индекс, который обозначает нулевой указатель, например. -1.

В основном мы здесь заменили указатели на индексы массива. Теперь при сериализации вы можете упорядочить этот индекс массива, как обычно. Вам не нужно беспокоиться о том, где объект будет находиться в памяти в целевой системе. Просто убедитесь, что у них одинаковый пул объектов.

Поэтому нам нужно сериализовать пулы объектов. Но какие? Ну, когда вы сериализуете граф объектов, вы не сериализуете только объект, вы сериализуете целую систему. Это означает, что сериализация системы не должна начинаться с частей системы. Эти объекты не должны беспокоиться о остальной системе, им нужно только сериализовать индексы массивов, и все. У вас должна быть процедура сериализации системы, которая организует сериализацию системы и проходит через соответствующие пулы объектов и сериализует их все.

На принимающей стороне все массивы и объекты внутри десериализуются, воссоздавая желаемый граф объектов.

Сериализация указателей функций

Не храните указатели в объекте. Имейте статический массив, который содержит указатели на эти функции и сохраняет индекс в объекте.

Поскольку обе программы имеют эту таблицу, скомпилированную в них, использование индекса должно работать.

Сериализация полиморфных типов

Поскольку я сказал, что вам следует избегать указателей в сериализуемых типах, и вместо этого вы должны использовать индексы массивов, polymorphism просто не может работать, потому что для этого требуются указатели.

Вам нужно работать с типом тегов и союзов.

Versioning

Вдобавок ко всему вышесказанное. Возможно, вам понадобятся разные версии программного обеспечения.

В этом случае каждый объект должен записать номер версии в начале их сериализации для указания версии.

При загрузке объекта с другой стороны более новые объекты, возможно, могут обрабатывать старые представления, но более старые не могут обрабатывать новые, поэтому они должны вызывать исключение из этого.

Каждый раз, когда что-то меняется, вы должны указывать номер версии.


Поэтому, чтобы обернуть это, сериализация может быть сложной. Но, к счастью, вам не нужно сериализовать все в вашей программе, чаще всего передаются только сообщения протокола, которые часто являются старыми старыми структурами. Поэтому вам не нужны сложные трюки, о которых я упоминал выше, слишком часто.

  • Преобразование байтов в Int / uint в C
  • Jackson ObjectMapper - указать порядок сортировки свойств объекта
  • Как добавить тип в белый список политики сериализации GWT?
  • Есть ли у .NET 4 встроенный сериализатор / десериализатор JSON?
  • JSON.Net Обнаружен собственный цикл привязки
  • Заставить JSON.NET включать миллисекунды при сериализации DateTime (даже если компонент ms равен нулю)
  • Deserialize JSON в динамический объект C #?
  • Может ли Json.NET сериализовать / десериализовать в / из streamа?
  • Специальная сериализация Jackson JSON для определенных полей
  • Невозможно десериализовать lambda
  • Django rest framework, используйте разные сериализаторы в том же ModelViewSet
  • Давайте будем гением компьютера.