Можно ли сериализовать и десериализовать класс в С++?

Можно ли сериализовать и десериализовать класс в С++?

Я использую Java уже 3 года, и сериализация/десериализация на этом языке довольно тривиальны. Есть ли у С++ аналогичные функции? Существуют ли встроенные библиотеки, обрабатывающие сериализацию?

Пример будет полезен.

Ответ 1

Библиотека Boost::serialization обрабатывает это довольно элегантно. Я использовал это в нескольких проектах. Там пример программы, показывающий, как ее использовать, здесь.

Единственный родной способ сделать это - использовать потоки. По сути, все, что делает библиотека Boost::serialization, расширяет потоковый метод, устанавливая платформу для записи объектов в текстовый формат и чтения их из того же формата.

Для встроенных типов или ваших собственных типов с правильно определенными operator<< и operator>>, это довольно просто; см. часто задаваемые вопросы по C++ для получения дополнительной информации.

Ответ 2

Я понимаю, что это старый пост, но он один из первых, который появляется при поиске c++ serialization.

Я призываю всех, кто имеет доступ к С++ 11, посмотреть cereal, только библиотеку заголовков С++ 11 для сериализации, которая поддерживает двоичные, JSON и XML из коробки. зерно было спроектировано так, чтобы его легко расширять и использовать и имеет аналогичный синтаксис для Boost.

Ответ 3

Boost - хорошее предложение. Но если вы хотите катиться самостоятельно, это не так сложно.

В принципе вам просто нужен способ построения графика объектов, а затем вывод их в какой-то структурированный формат хранения (JSON, XML, YAML, что угодно). Создание графика так же просто, как использование рекурсивного алгоритма достойного объекта маркировки, а затем вывод всех отмеченных объектов.

Я написал статью, описывающую рудиментарную (но все же мощную) систему сериализации. Вам может показаться интересным: Использование SQLite в качестве формата файла на диске, часть 2.

Ответ 4

Что касается "встроенных" библиотек, то << и >> зарезервированы специально для сериализации.

Вы должны переопределить << для вывода своего объекта в некоторый контекст сериализации (обычно iostream) и >> для чтения данных из этого контекста. Каждый объект отвечает за вывод его агрегированных дочерних объектов.

Этот метод работает отлично, пока граф объекта не содержит циклов.

Если это так, вам придется использовать библиотеку для обработки этих циклов.

Ответ 5

Я рекомендую Google протокольные буферы. У меня была возможность проверить диск библиотеки на новый проект, и он замечательно прост в использовании. Библиотека сильно оптимизирована для производительности.

Protobuf отличается от других решений сериализации, упомянутых здесь, в том смысле, что он не сериализует ваши объекты, а скорее генерирует код для объектов, которые сериализуются в соответствии с вашими спецификациями.

Ответ 6

Boost:: serialization - отличный вариант, но я столкнулся с новым проектом: Cereal который я нахожу намного более элегантным! Я настоятельно рекомендую исследовать его.

Ответ 7

Вы можете проверить протокол amef, примером будет кодировка С++ в amef,

    //Create a new AMEF object
    AMEFObject *object = new AMEFObject();

    //Add a child string object
    object->addPacket("This is the Automated Message Exchange Format Object property!!","adasd");   

    //Add a child integer object
    object->addPacket(21213);

    //Add a child boolean object
    object->addPacket(true);

    AMEFObject *object2 = new AMEFObject();
    string j = "This is the property of a nested Automated Message Exchange Format Object";
    object2->addPacket(j);
    object2->addPacket(134123);
    object2->addPacket(false);

    //Add a child character object
    object2->addPacket('d');

    //Add a child AMEF Object
    object->addPacket(object2);

    //Encode the AMEF obejct
    string str = new AMEFEncoder()->encode(object,false);

Декодирование в java было бы таким,

    string arr = amef encoded byte array value;
    AMEFDecoder decoder = new AMEFDecoder()
    AMEFObject object1 = AMEFDecoder.decode(arr,true);

Реализация протокола имеет кодеки как для С++, так и для Java, интересная часть заключается в том, что она может сохранять представление класса объектов в виде пар значений имени, Мне нужен аналогичный протокол в моем последнем проекте, когда я случайно наткнулся на этот протокол, я фактически модифицировал базовую библиотеку в соответствии с моими требованиями. Надеюсь, это поможет вам.

Ответ 8

Я рекомендую использовать сериализацию boost, как описано другими плакатами. Вот хороший подробный учебник о том, как его использовать, который прекрасно дополняет обучающие материалы: http://www.ocoudert.com/blog/2011/07/09/a-practical-guide-to-c-serialization/

Ответ 9

Sweet Persist - это еще один.

Можно сериализоваться в потоках и из потоков в форматах XML, JSON, Lua и двоичных форматах.

Ответ 10

Я предлагаю изучить абстрактные фабрики, которые часто используются в качестве основы для сериализации

Я ответил в другом вопросе о С++ фабриках. См. там, если интересен гибкий factory. Я пытаюсь описать старый путь из ET ++, чтобы использовать макросы, которые отлично поработали для меня.

ET ++ был проектом для переноса старого MacApp на С++ и X11. В попытке этого Эрик Гамма и т.д. Начал думать о Design Patterns. ET ++ содержит автоматические способы сериализации и интроспекции во время выполнения.

Ответ 11

Если вам нужна простая и лучшая производительность, и вам не нужна обратная совместимость данных, попробуйте HPS, она будет легкой, намного быстрее, чем Boost и т.д., И будет намного проще в использовании, чем Protobuf и т.д.

Пример:

std::vector<int> data({22, 333, -4444});
std::string serialized = hps::serialize_to_string(data);
auto parsed = hps::parse_from_string<std::vector<int>>(serialized);

Ответ 12

Вот простая библиотека сериализатора, которую я собрал. Только заголовок, с11 и есть примеры для сериализации основных типов. Здесь один для карты в класс.

https://github.com/goblinhack/simple-c-plus-plus-serializer

#include "c_plus_plus_serializer.h"

class Custom {
public:
    int a;
    std::string b;
    std::vector c;

    friend std::ostream& operator<<(std::ostream &out, 
                                    Bits my)
    {
        out << bits(my.t.a) << bits(my.t.b) << bits(my.t.c);
        return (out);
    }

    friend std::istream& operator>>(std::istream &in, 
                                    Bits my)
    {
        in >> bits(my.t.a) >> bits(my.t.b) >> bits(my.t.c);
        return (in);
    }

    friend std::ostream& operator<<(std::ostream &out, 
                                    class Custom &my)
    {
        out << "a:" << my.a << " b:" << my.b;

        out << " c:[" << my.c.size() << " elems]:";
        for (auto v : my.c) {
            out << v << " ";
        }
        out << std::endl;

        return (out);
    }
};

static void save_map_key_string_value_custom (const std::string filename)
{
    std::cout << "save to " << filename << std::endl;
    std::ofstream out(filename, std::ios::binary );

    std::map< std::string, class Custom > m;

    auto c1 = Custom();
    c1.a = 1;
    c1.b = "hello";
    std::initializer_list L1 = {"vec-elem1", "vec-elem2"};
    std::vector l1(L1);
    c1.c = l1;

    auto c2 = Custom();
    c2.a = 2;
    c2.b = "there";
    std::initializer_list L2 = {"vec-elem3", "vec-elem4"};
    std::vector l2(L2);
    c2.c = l2;

    m.insert(std::make_pair(std::string("key1"), c1));
    m.insert(std::make_pair(std::string("key2"), c2));

    out << bits(m);
}

static void load_map_key_string_value_custom (const std::string filename)
{
    std::cout << "read from " << filename << std::endl;
    std::ifstream in(filename);

    std::map< std::string, class Custom > m;

    in >> bits(m);
    std::cout << std::endl;

    std::cout << "m = " << m.size() << " list-elems { " << std::endl;
    for (auto i : m) {
        std::cout << "    [" << i.first << "] = " << i.second;
    }
    std::cout << "}" << std::endl;
}

void map_custom_class_example (void)
{
    std::cout << "map key string, value class" << std::endl;
    std::cout << "============================" << std::endl;
    save_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
    load_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
    std::cout << std::endl;
}

Выход:

map key string, value class
============================
save to map_of_custom_class.bin
read from map_of_custom_class.bin

m = 2 list-elems {
    [key1] = a:1 b:hello c:[2 elems]:vec-elem1 vec-elem2
    [key2] = a:2 b:there c:[2 elems]:vec-elem3 vec-elem4
}

Ответ 13

Я использую следующий шаблон для реализации сериализации:

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 - это фиктивный тип, чтобы различать разные виды сериализации, например. одно и то же целое число может быть сериализовано как little-endian, big endian, varint и т.д.

По умолчанию 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());

Из-за абстрактной логики итератора он должен работать с любым итератором (например, потоковыми итераторами), указателем и т.д.