Как сериализовать объект в C++?
У меня есть небольшая иерархия объектов, которые мне нужно сериализовать и передать через соединение сокета. Мне нужно как сериализовать объект, так и десериализовать его на основе того, какой он тип. Есть ли простой способ сделать это в C++ (как есть в Java)?
есть ли какие-либо примеры кода сериализации C++ в интернете или учебные пособия?
EDIT: просто чтобы быть ясно, я ищу методы преобразования объекта в массив байтов, а затем обратно в объект. Я смогите отрегулировать передачу гнезда.
3 ответов:
говоря о сериализации повысить API сериализации приходит мне на ум. Что касается передачи сериализованных данных по сети, я бы использовал сокеты Berkeley или библиотека asio.
Edit:
Если вы хотите сериализовать свои объекты в массив байтов, вы можете использовать сериализатор boost следующим образом (взятый с сайта учебника):#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)или аналогичная сигнатурная функция, которая будет писать свой двоичный код представительство в векторе. Затем эта функция может передать этот вектор вниз к своим функциям сериализации членов, чтобы они могли записывать свои вещи в него тоже.так как представление данных может быть различным на разных архитектурах. Вам нужно выяснить схему, как представлять данные.
давайте начнем с основ:
сериализация данных integer
просто напишите байты в маленьком порядке endian. Или использовать варинт представление, если размер имеет значение.
сериализация в маленькой прямом порядке:
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 имеет монополию здесь. Я не знаю никакой основной архитектуры, которая использовала бы что-то еще для поплавков. Единственное что может отличаться-это порядок байт. На некоторых архитектурах используется прямой порядок байтов, другие используют большой байтов с обратным порядком байтов порядок. Это означает, что вам нужно быть осторожным, какой заказ вам громче байтов на приемном конце. Еще одним отличием может быть обработка значений denormal и infinity и NAN. Но пока вы избегаете этих значений, вы должны быть в порядке.
сериализация:
uint8_t mem[8]; memcpy(mem, doubleValue, 8); data.push_back(mem[0]); data.push_back(mem[1]); ...десериализация делает это в обратном направлении. Обратите внимание на порядок байтов вашей архитектуры!
сериализации строки
сначала вам нужно договориться о кодировке. UTF-8 является общим. Затем сохраните его как префикс длины: сначала вы сохраняете длину строки с помощью метода, о котором я упоминал выше, а затем записываете строку байт за байтом.
сериализовать массивы.
они такие же, как струны. Сначала вы сериализуете целое число, представляющее размер массива, а затем сериализуете каждый объект в нем.
сериализовать все объекты
как я уже говорил, они должны иметь
serializeметод, который добавляет содержимое к вектору. Чтобы восстановить объект, он должен иметь конструктор, который принимает поток байтов. Это может бытьistreamно в простейшем случае это может быть просто ссылка
Comments