I have this class I want to serialize: (the private attr’s)
class Task {
public:
enum Status { COMPLETED, PENDIENT };
// GETTERS SETTERS...
// UTILS
const std::ostream& storeTask( std::ostream &stream );
const std::istream& retrieveTask( std::istream &stream );
private:
void setID();
static int sCount;
int id;
std::string text;
Status status;
tm timestamp;
};
As you can see, I’m trying to do so by implementing a storeTask and retrieveTask method wich I defined like this:
// SERIALIZATION
const std::ostream& Task::storeTask( std::ostream &stream ) {
stream.write((char *) &id, sizeof(int));
stream.write((char *) text.c_str(), sizeof(text.length()));
stream.write((char *) &status, sizeof(Status));
stream.write((char *) ×tamp, sizeof(tm));
return stream;
}
const std::istream& Task::retrieveTask( std::istream &stream ) {
stream.read((char *) &id, sizeof(int));
stream.read((char *) text.c_str(), sizeof(text.length()));
stream.read((char *) &status, sizeof(Status));
stream.read((char *) ×tamp, sizeof(tm));
return stream;
}
I’m testing the implementation by creating a vector of 2 Task objects, writing them using their storeTask method, then creating another Task vector including 2 different Task’s and trying to reload the value of the Task vector number one to the second vector.
Writing the binary file with both tasks seems to work just fine, however when reading them again, it only fetches the 4 first char’s of the text string.
int main() {
std::vector<Task> myTasks;
Task task1("First Task");
Task task2("Second Task");
myTasks.push_back(task1);
myTasks.push_back(task2);
std::vector<Task> myTasks2;
Task task3("Task num1");
Task task4("Task num2");
myTasks2.push_back(task3);
myTasks2.push_back(task4);
writeTasks(myTasks);
readTasks(myTasks2);
return 0;
}
void writeTasks(std::vector<Task> aTasks) {
std::fstream fileStream("fileStream", std::ios::out | std::ios::binary);
for ( int x = 0; x < aTasks.size(); x++ ) {
std::cout << "Storing: " << aTasks[x].getText() << "\n";
aTasks[x].storeTask(fileStream);
}
fileStream.close();
}
void readTasks(std::vector<Task> aTasks) {
std::fstream fileStream;
fileStream.open("fileStream", std::ios::in | std::ios::binary);
for ( int x = 0; x < aTasks.size(); x++ ) {
std::cout << "Retrieving " << aTasks[x].getText();
aTasks[x].retrieveTask(fileStream);
std::cout << " as --> " << aTasks[x].getText() << " - " << aTasks[x].getTime() << "\n";
}
fileStream.close();
}
And that returns:
Storing: First Task
Storing: Second Task
Retrieving Task num1 as --> Firs num1 - 01:02:26
Retrieving Task num2 as --> Seco num2 - 01:02:26
When it should return:
Storing: First Task
Storing: Second Task
Retrieving Task num1 as --> First Task - 01:02:26
Retrieving Task num2 as --> Second Task - 01:02:26
Any ideas? Don’t know if the problem is on writing the binary serialization or reading it…
Other values, like time, date seems to work fine.
As text.c_str() returns a const char *, I’ve declared a little function to convert from string to char *:
char * str_to_char(std::string s) {
char *a=new char[s.size()+1];
a[s.size()]=0;
memcpy(a,s.c_str(),s.size());
return a;
}
And tried this in the retireveTask method:
stream.read(str_to_char(text), text.size() + 1);
But got:
Storing: First Task
Storing: Second Task
Retrieving Task num1 as --> Task num1 - 589824:524288:131072
Retrieving Task num2 as --> Task num2 - 112:09:08
You have two major problems:
When writing you write
sizeof(text.length())). Thatsizeofoperator returns the size ofstd::string::size_typereturned bytext.length()(normally 4 or 8 bytes). Use onlytext.length().When reading, you try to read text put directly into the string. This will most definitely not work as the actual string may not even be allocated, or not have enough space allocated. I would say that you are lucky you manage to read something at all and your program doesn’t crash here.
To serialize a string (or any variable-sized data) you need to store the length of the string first, so you know how much to read later.
Proper serialization is not easy, and I really recommend you look into Boost serialization which handles all the strangeness and corner-cases correctly.