усилваща сериализация, десериализация на необработени 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