views:

277

answers:

1

Hi all,

I have a problem using boost serialization using binary archives. It works when using a file stream but I want to store it in my local variable and ultimately save/load it to/from berkeley db. When executing the program I get a *boost::archive::archive_exception*: 'stream error' when instantiating the *binary_iarchive*.

#include <sys/time.h>
#include <string>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
#include <sstream>

namespace boost {
namespace serialization {

template<class Archive>
void serialize(Archive & ar, timeval & t, const unsigned int version)
{
    ar & t.tv_sec;
    ar & t.tv_usec;
}

}//namespace serialization
}//namespace boost


int main(int, char**)
{
    timeval t1;
    gettimeofday(&t1, NULL);
    char buf[256];

    std::stringstream os(std::ios_base::binary| std::ios_base::out| std::ios_base::in);
    {
        boost::archive::binary_oarchive oa(os, boost::archive::no_header);
        oa << t1;
    }

    memcpy(buf, os.str().data(), os.str().length());
    if(memcmp(buf, os.str().data(), os.str().length()) != 0)
        printf("memcpy error\n");

    timeval t2;
    {
        std::stringstream is(buf, std::ios_base::binary| std::ios_base::out| std::ios_base::in);

        boost::archive::binary_iarchive ia(is, boost::archive::no_header);
        ia >> t2;
    }

    printf("Old(%d.%d) vs New(%d.%d)\n", t1.tv_sec, t1.tv_usec, t2.tv_sec, t2.tv_usec);

    return 0;
}

It works when initializing is with os.str(), so I guess my way of copying the data to my buffer or to is is wrong.

+3  A: 

Well, for one thing .data() doesn't have a terminal \0. It's not a c-string. I didn't even realize stringstream had a char* constructor (who in their right mind uses them anymore?) but apparently it does and I'd bet it expects \0.

Why are you trying to do it that way anyway? You're much better off working in C++ strings. Initialize is with os.str().

Edit: binary data contains lots of \0 characters and the std::string(char*) constructor stops at the first one. Your deserialization routine will then inevitably try to read past the end of the stream (because it isn't complete). Use the iterator constructor for std::string when you pass buf into the stringstream.

std::stringstream is(std::string(buf, buf+os.str().length()), flags);
Noah Roberts
I already tried to use a c-string but that didn't work either. I don't use c++ strings because I need to get the raw data to store it in my berkeley db. The aim of this is to save serialized data in the berkeley db and later retrieve and unserialize it.
I was correct. There is no non-string constructor for stringstream. Your code attempts to create one through implicit casting. You could try explicitly casting by passing std::string(buf, buf+os.str().length()) instead of buf if you really insist. This should fix your problem since the char* constructor for std::string is going to stop at the *first* \0 it finds and your binary data is probably riddled with them.
Noah Roberts
works, thanks for the help!