Search code examples
c++serializationboostboost-serialization

boost serialization, deserialization of raw C arrays


I'm trying to serialize and deserialize raw C pointers and their data, with the example below. It seems to serialize just fine, but I am unsure how to make it deserialize - it just crashes with a memory access violation exception when I deserialize it. I suppose it is because it dosn't know how to deserialize it, but where do I specify that?

Using a vector is not an option, in very large primitive data amounts it is painfully slow

#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;
}

Solution

  • I heartily recommend you use std::array or std::vector here, because... you messed this up :)

    For starters, Monkey doesn't initialize its members. So, loading ends up doing a load_binary to whatever pointer value m.arr happened to have. How would you expect the deserialization to "know" that you needed to allocate memory for that? You need to tell it:

        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);
        }
    

    Now, let's make Monkey a bit less unsafe (by adding initialization and destruction, and, perhaps most importantly, prohibiting copy semantics):

    struct Monkey
    {
        uint32_t num;
        float* arr;
    
        Monkey() : num(0u), arr(nullptr) {}
    
        Monkey(Monkey const&) = delete;
        Monkey& operator=(Monkey const&) = delete;
        ~Monkey() { delete[] arr; }
    };
    

    Now, you can see it work:

    #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, ";"));
    }
    

    Prints

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

    Live on Coliru