ускоренная сериализация, десериализация необработанных массивов C

Я пытаюсь сериализовать и десериализовать необработанные указатели C и их данные с помощью приведенного ниже примера. Кажется, что он сериализуется просто отлично, но я не уверен, как его десериализовать - он просто падает с исключением нарушения доступа к памяти, когда я его десериализую. Я полагаю, это потому, что он не знает, как его десериализовать, но где мне это указать?

Использование вектора не вариант, в очень больших объемах примитивных данных это мучительно медленно

#include <stdint.h>
#include <string>
#include <iostream>
#include <fstream>
#pragma warning (push) 
#pragma warning( disable : 4244 ) 
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/array.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#pragma warning (pop) 

struct Monkey
{
    uint32_t num;
    float* arr;

};


namespace boost
{
    namespace serialization
    {
        template<class Archive>
        void serialize(Archive & ar, Monkey& m, const unsigned int version)
        {
            ar & m.num;
            ar & make_array<float>(m.arr, m.num);
        }
    }
}


int _tmain(int argc, _TCHAR* argv[])
{
    const char* name = "monkey.txt";

    {
        Monkey m;
        m.num = 10;
        m.arr = new float[m.num];
        for (uint32_t index = 0; index < m.num; index++)
            m.arr[index] = (float)index;

        std::ofstream outStream(name, std::ios::out | std::ios::binary | std::ios::trunc);
        boost::archive::binary_oarchive oar(outStream);
        oar << (m);
    }

    Monkey m;
    std::ifstream inStream(name, std::ios::in | std::ios::binary);     
    boost::archive::binary_iarchive iar(inStream);
    iar >> (m);

    return 0;
}

person KaiserJohaan    schedule 15.12.2013    source источник


Ответы (2)


Я искренне рекомендую вам использовать здесь std::array или std::vector, потому что... вы все испортили :)

Во-первых, Monkey не инициализирует свои члены. Таким образом, загрузка завершается выполнением load_binary любого значения указателя m.arr. Как вы ожидаете, что десериализация «узнает», что вам нужно выделить для этого память? Вам нужно сказать это:

    template<class Archive>
    void serialize(Archive & ar, Monkey& m, const unsigned int version)
    {
        ar & m.num;
        if (Archive::is_loading::value)
        {
            assert(m.arr == nullptr);
            m.arr = new float[m.num];
        }
        ar & make_array<float>(m.arr, m.num);
    }

Теперь давайте сделаем Monkey немного менее небезопасным (добавив инициализацию и уничтожение и, возможно, самое главное, запретив семантику копирования):

struct Monkey
{
    uint32_t num;
    float* arr;

    Monkey() : num(0u), arr(nullptr) {}

    Monkey(Monkey const&) = delete;
    Monkey& operator=(Monkey const&) = delete;
    ~Monkey() { delete[] arr; }
};

Теперь вы можете видеть, как это работает:

#include <iostream>
#include <fstream>
#pragma warning(disable: 4244)
#include <boost/serialization/serialization.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>

struct Monkey
{
    uint32_t num;
    float* arr;

    Monkey() : num(0u), arr(nullptr) {}

    Monkey(Monkey const&) = delete;
    Monkey& operator=(Monkey const&) = delete;
    ~Monkey() { delete[] arr; }
};

namespace boost
{
    namespace serialization
    {
        template<class Archive>
        void serialize(Archive & ar, Monkey& m, const unsigned int version)
        {
            ar & m.num;
            if (Archive::is_loading::value)
            {
                assert(m.arr == nullptr);
                m.arr = new float[m.num];
            }
            ar & make_array<float>(m.arr, m.num);
        }
    }
}

int main(int argc, char* argv[])
{
    const char* name = "monkey.txt";
    {
        Monkey m;
        m.num = 10;
        m.arr = new float[m.num];
        for (uint32_t index = 0; index < m.num; index++)
            m.arr[index] = (float)index;

        std::ofstream outStream(name, std::ios::out | std::ios::binary | std::ios::trunc);
        boost::archive::binary_oarchive oar(outStream);
        oar << (m);
    }

    Monkey m;
    std::ifstream inStream(name, std::ios::in | std::ios::binary);
    boost::archive::binary_iarchive iar(inStream);
    iar >> (m);

    std::copy(m.arr, m.arr + m.num, std::ostream_iterator<float>(std::cout, ";"));
}

Отпечатки

0;1;2;3;4;5;6;7;8;9;

Прямая трансляция на Coliru

person sehe    schedule 15.12.2013
comment
Я начал писать свой собственный ответ, но @sehe уже разместил весь код и все проблемы. Я могу только добавить свое резюме, просто перефразировав то, что здесь: ваша настоящая проблема не в десериализации как таковой, а в управлении памятью. Вы не указали, как структура Monkey выделяет, копирует или освобождает используемую ею память. Sehe предоставил два чистых способа решить эту проблему управления памятью (использовать вектор или добавить/удалить копию ctor, dtor и т. д.). - person Michael Simbirsky; 16.12.2013

При десериализации m.arr инициализируется не массивом из 10 чисел с плавающей запятой, а массивом float*.

Сделайте Monkey::arr std::vector<float> вместо float*. Сериализация Boost знает, как сериализовать и десериализовать все контейнеры из стандартной библиотеки C++.

person Oswald    schedule 15.12.2013
comment
Как/где мне выделить эту память, учитывая, что m.num может быть любым произвольным числом, которое я не знаю, пока не десериализую его? - person KaiserJohaan; 15.12.2013
comment
Я не знаю. Но вы могли бы сделать Monkey::arr std::vector<float> вместо float*. Сериализация Boost знает, как сериализовать и десериализовать все контейнеры из стандартной библиотеки C++. - person Oswald; 15.12.2013
comment
К сожалению, я не могу этого сделать; как я писал перед фрагментом кода, я имею дело с большими объемами данных (много 3D-сеток и данных изображений), и их десериализация слишком медленная (минуты!) - person KaiserJohaan; 15.12.2013
comment
Я не могу придумать никакой причины, по которой десериализация в массив должна быть быстрее, чем десериализация в std::vector (за исключением чрезвычайно плохого кодирования на стороне повышения, но это довольно маловероятно). Если код в данный момент работает быстро, это потому, что вы не десериализуете весь массив. - person Oswald; 16.12.2013
comment
Я полностью согласен с Освальдом. Это миф, что десериализация в вектор будет медленнее. Полный миф, поскольку библиотека даже гарантирует, что .reserve(n) вызывается с фактическими размерами перед загрузкой данных. И типы POD загружаются с использованием оптимизированной копии (эквивалент memcpy) - person sehe; 16.12.2013
comment
Причина отсутствия вектора заключалась в том, что у меня были проблемы в сборках отладки только с итераторами векторов, которые, похоже, не были исправлены в VS2012, независимо от того, пытались ли вы отключить флаги отладки. Релизные сборки были приемлемыми скоростями - person KaiserJohaan; 16.12.2013