0

I am attempting to read and write a vector of structs into a file. Iterating through each struct, first I write the size of the following struct, then I write the structure. I encounter crashes when reading the file.

Structure:

struct Book {

   string author;
   string title;
   string isbn;
   int copies;
   Status status;

};

Loading from file:

void load_books(vector<Book> *books) {
ifstream readFile;
int read_size;
Book temp;

readFile.open(BOOK_DB_FILE.c_str(), ios::in | ios::binary) ;

while(readFile.read(reinterpret_cast<char*> (&read_size), sizeof(read_size))) {
    cout << read_size << endl;

    if (!readFile.read(reinterpret_cast<char*> (&temp), read_size)) {
        cout << "Failed to read library.dat!" << endl;
    }

    cout << "Attempting to load book..." << endl;
    cout << temp.title << ":" << temp.copies << endl;

    (*books).push_back(temp);

}
cout << "Loaded books!" << endl;

}

Writing to file:

void save_books(vector<Book> *books) {
ofstream writeFile;
int write_size;

writeFile.open(BOOK_DB_FILE.c_str(), ios::out | ios::binary);

for (int ind = 0 ; ind < (*books).size(); ind++) {
    Book book = (*books)[ind];
    write_size = sizeof(book);

    cout << "Writing book with size: " << write_size << endl;
    cout << book.copies << " of " << book.title << " by " << book.author << endl;

    writeFile.write(reinterpret_cast<char*>(&write_size), sizeof(write_size));
    writeFile.write(reinterpret_cast<char*>(books + ind), write_size);
}

}
7
  • Note: The crash occurs right after cout << "Attempting to load book..." << endl; is called... Commented May 11, 2016 at 20:46
  • I strongly recommend you take a look at Boost.Serialization. Commented May 11, 2016 at 20:48
  • This is for a class project, and I'm not allowed to use boost. Commented May 11, 2016 at 20:50
  • Why oh why do they give these "binary file" writing / reading assignments, when it will not work? Your Book is not a POD type, thus it cannot be written to a binary file in the fashion that you're writing it. You cannot write Book in just a binary blob like that -- it has to be serialized, meaning you must write the data to the file, not the object. (I see this question or variations of it over and over again, especially from students). Commented May 11, 2016 at 21:00
  • Is it the string inclusion that prevents it from being a POD? I was under the impression that I could serialize it like so if I preceded it with the size of the struct. Commented May 11, 2016 at 21:01

1 Answer 1

1

In general I would always recommend to use a library such as Boost.Serialization, but alas, you're not allowed to use Boost (or any other serialization library, I guess). So we'll look at your current approach instead.

While your method of dumping the binary object layout straight to disk is fraught with risk (e.g. because of platform dependencies like endianness), I'll assume that for a mere class project we can live with these deficiencies.

Your main problem, right now, is that you are dumping the struct Book memory representation to disk and reading it back in, but you are overlooking the fact that the std::string fields are not handled like that.

Internally, std::string is not a POD, but (roughly) another class which allocates separate memory for the actual string data (for sake of illustration, assume it carries a char* pointer in its implementation). You are currently not storing this data; you are only storing the values of std::string's internal pointers, and reading them back when loading. At that point, these internal pointers are pointing nowhere meaningful.

For a rough idea of what you need to do, you will have to explicitly and individually serialize/deserialize all members of your Book class recursively. Roughly like this:

  1. Store the length, and the raw string data of author
  2. Store the length, and the raw string data of title
  3. Store the length, and the raw string data of isbn
  4. Store the integer copies
  5. Store the enum value of status

And when reading, load every member in the same way. So you will need a serialzer/deserializer function for every "basic" type (in your case, std::string, int and Status), and "combining" serializer and deserializer functions for Book which call the base members' serialization/deserialization functions in turn.

This is also roughly what Boost.Serialization does internally, so I recommend you have a look at it anyway. Good luck!

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

4 Comments

Thanks for the quick answer. I'm also assuming I could use a c-string in the Book struct instead.
Of course you could use a c-string, and then it would be obvious that it's a pointer and that serializing a pointer doesn't make much sense, right? I would stick with std::string, though; it's the proper C++ type choice, and you have to write special (de)serialization code either way.
I'd imagine using a c-string would work, as the ofstream::write method takes a pointer anyways?
Either way, I've implemented what you mentioned and it works.

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.