0

I'm implementing a very simple serialization library, to save/restore user_info object, which contains an int and a std::string, here's my code:

#include <iostream>
#include <sstream>
using namespace std;

struct user_info {
    int id;
    string name;
};

// for any POD type like int, read sizeof(int) from the stream
template <class stream_t, class T>
void de_serialize(stream_t& stream, T& x) {
    stream.read((char*)&x, sizeof(T));
}

// for std::string, read length(int) first, then read string content when length>0
template <class stream_t>
void de_serialize(stream_t& stream, std::string& str) {
    int len;
    de_serialize(stream, len);
    str.resize(len);
    char x;
    for(int i=0; i<len; i++) {
        de_serialize(stream, x);
        str[i] = x;
    }
}

// read from stream, fill id and name
template <typename stream_t>
void de_serialize(stream_t& ss, user_info& ui) {
    de_serialize(ss, ui.id);
    de_serialize(ss, ui.name);
}


int main() {
    // read from file, but here I use a 8-bytes-content represents the file content
    stringstream ifs;
    // two int, \x1\x1\x1\x1 for id, \x0\x0\x0\x0 for name
    ifs.write("\x1\x1\x1\x1\x0\x0\x0\x0", 8);
    while(!ifs.eof()) {
        user_info ui;
        de_serialize(ifs, ui);
        // when first time goes here, the stream should've read 8 bytes and reaches eof, 
        // then break the while loop, but it doesn't
        // so the second time it calls de_serialize, the program crashes
    }
}

the code illustrates the part of de-serializing. The while loop is supposed to run once, and the stream reaches eof, but why it doesn't stop looping? The second loop causes a crash.

Please refer to the comments, thanks.

aj3423
  • 1,629
  • 3
  • 21
  • 52
  • 1
    `eof()` is set when the stream reaches EOF *by not being able to read more data*. If you read 8 bytes from an 8-byte stream, you were able to read them all, so `eof()` is *not* set. And cannot be - how would the stream know there's nothing there *without looking?* See this FAQ question: http://stackoverflow.com/q/5605125/1782465 – Angew is no longer proud of SO Sep 25 '14 at 10:25

1 Answers1

1

The eof() flag will never be set if the streem develops an error condition. It is usually wrong to loop on eof(). What I would do here is change the return type from your de_serialize() function to return the stream and then rewrite your loop around the de_serialize() function

Like this:

#include <iostream>
#include <sstream>
using namespace std;

struct user_info {
    int id;
    string name;
};

// for any POD type like int, read sizeof(int) from the stream
template <class stream_t, class T>
stream_t& de_serialize(stream_t& stream, T& x) {
    stream.read((char*)&x, sizeof(T));
    return stream; // return the stream
}

// for std::string, read length(int) first, then read string content when length>0
template <class stream_t>
stream_t& de_serialize(stream_t& stream, std::string& str) {
    int len;
    de_serialize(stream, len);
    str.resize(len);
    char x;
    for(int i=0; i<len; i++) {
        de_serialize(stream, x);
        str[i] = x;
    }
    return stream; // return the stream
}

// read from stream, fill id and name
template <typename stream_t>
stream_t& de_serialize(stream_t& ss, user_info& ui) {
    de_serialize(ss, ui.id);
    de_serialize(ss, ui.name);
    return ss; // return the stream
}


int main() {
    // read from file, but here I use a 8-bytes-content represents the file content
    stringstream ifs;
    // two int, \x1\x1\x1\x1 for id, \x0\x0\x0\x0 for name
    ifs.write("\x1\x1\x1\x1\x0\x0\x0\x0", 8);

    user_info ui;
    while(de_serialize(ifs, ui)) { // loop on de_serialize()

        // Now you know ui was correctly read from the stream

        // when first time goes here, the stream should've read 8 bytes and reaches eof,
        // then break the while loop, but it doesn't
        // so the second time it calls de_serialize, the program crashes
    }
}
Galik
  • 42,526
  • 3
  • 76
  • 100
  • Thanks for the quick reply, but the **operator bool()** doesn't work either, it's still looping. Is there any function like "stream.has_more_data()"? – aj3423 Sep 25 '14 at 10:35
  • @aj3423 You have a `for()` loop in one of your `de_serialize()` functions. Maybe you could validate that it has a sensible terminal value. If it is read as garbage it could be a large value perhaps? – Galik Sep 25 '14 at 10:39
  • In your function you could have something like: `if(!de_serialize(stream, len) && len < MAXLEN) len = 0;` etc... – Galik Sep 25 '14 at 10:40
  • Use a len variable, it's the original idea, I tried to avoid this variable, but it seems impossible, now I serialize/deserialize the **len** first and then the array, thanks for your help. – aj3423 Sep 25 '14 at 11:17