25

Boost serialization doc's assert that the way to serialize/deserialize items is using a binary/text archive with a stream on the underlying structure. This works fine if I wan't to use the serialized data as an std::string, but my intention is to convert it directly to a char* buffer. How can I achieve this without creating a temporary string?

Solved! For the ones that wanted a example:

char buffer[4096];

boost::iostreams::basic_array_sink<char> sr(buffer, buffer_size);  
boost::iostreams::stream< boost::iostreams::basic_array_sink<char> > source(sr);

boost::archive::binary_oarchive oa(source);

oa << serializable_object; 
2
  • On the other hand, why would you relinquish the free memory management and risk leak and overrun :) ? Commented Jun 10, 2010 at 15:10
  • Sadly, because of performance issues :( Commented Jun 10, 2010 at 15:20

3 Answers 3

35

If you do not know the size of the data you are sending in advance, this is a generic way to serialize into an std::string:

// serialize obj into an std::string
std::string serial_str;
boost::iostreams::back_insert_device<std::string> inserter(serial_str);
boost::iostreams::stream<boost::iostreams::back_insert_device<std::string> > s(inserter);
boost::archive::binary_oarchive oa(s);

oa << obj;

// don't forget to flush the stream to finish writing into the buffer
s.flush();

// now you get to const char* with serial_str.data() or serial_str.c_str()

To deserialize, use

// wrap buffer inside a stream and deserialize serial_str into obj
boost::iostreams::basic_array_source<char> device(serial_str.data(), serial_str.size());
boost::iostreams::stream<boost::iostreams::basic_array_source<char> > s(device);
boost::archive::binary_iarchive ia(s);
ia >> obj;

This works like a charm, I use this to send data around with MPI.

This can be done very fast if you keep the serial_str in memory, and just call serial_str.clear() before you serialize into it. This clears the data but does not free any memory, so no allocation will happen when your next serialization data size does not require it.

Sign up to request clarification or add additional context in comments.

7 Comments

Great answer! I'm attemping to keep the buffer in memory as you suggest, but it seems calling serial_str.clear() is not enough - do I also need to reset to binary_orachive or the back_inserter_device somehow, to tell it to go back to the writing in the beginning of serial_str?
@omer, in my code I have just std::string serial_str as a member variable, and re-create everything else every time I send something.
How do i get the size of serial_str.data()? serial_str.size() does not work, because it cuts off at the first NULL character right?
Nope, std::string is not null terminated, so size() works as intended.
Check my answer below
|
6

IIUC, you would like to write to a preallocated array of fixed size.

You could use a boost::iostreams::array_sink (wrapped with stream to give it an std::ostream interface) for that.

1 Comment

@op: Thanks, that worked like a charm! I just wanted that boost had more documentation :(
1

Simpler version than accepted answer using std::stringstream :

// access data with .data() and size with .size()
using RawDataBuffer = std::string;

RawDataBuffer serialize(const Foo &obj) {
    std::stringstream ss;
    boost::archive::binary_oarchive oa(ss);

    oa << obj;

    return ss.str();
}

Foo deserialize(const RawDataBuffer &data) {
    std::stringstream ss(data);
    boost::archive::binary_iarchive ia(ss);

    Foo obj; // Foo must be default-constructible
    ia >> obj;

    return obj;
}


Full working example compiled with boost 1.66 :

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/access.hpp>
#include <sstream>
#include <iostream>


class Foo {
public:
    Foo() = default;
    Foo(int i) : _i(i)
    {}

    int get() const
    { return _i; }

protected:
    friend class boost::serialization::access;

    template<class Archive>
    void serialize(Archive &ar, const unsigned int /* version */ )
    {
        ar & _i;
    }

private:
    int _i;
};


// access data with .data() and size with .size()
using RawDataBuffer = std::string;

RawDataBuffer serialize(const Foo &obj) {
    std::stringstream ss;
    boost::archive::binary_oarchive oa(ss);

    oa << obj;

    return ss.str();
}

Foo deserialize(const RawDataBuffer &data) {
    std::stringstream ss(data);
    boost::archive::binary_iarchive ia(ss);

    Foo obj; // Foo must be default-constructible
    ia >> obj;

    return obj;
}


int main()
{
    RawDataBuffer buff;

    {
        Foo fortyTwo(42);

        buff = serialize(fortyTwo);
    }
    {
        Foo reborn;

        reborn = deserialize(buff);

        std::cout << "Reborn from " << reborn.get() << std::endl;
    }
}

1 Comment

ss.str() returns a copy of the serialised data. It's very inefficient. That changes in C++20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.