0

I am running in to an issue while reading/writing a struct having complex data specifically string. my struct looks like below.

struct MyRecord {
  char name[80];
  string location;     // <<== note this 
  double balance;
  unsigned long account_num;
};

I use different functions for reading and writing.

This is the code I am using to write the data to file

struct MyRecord acc;

strcpy(acc.name, "R");
acc.location = "newlocation ok";    // <<== note this 
acc.balance = 1.3;
acc.account_num = 34;

ofstream outbal("balance", ios::out | ios::binary);
outbal.write((char *) &acc, sizeof(struct MyRecord));

same like I am using the below code to read from file.

ifstream inbal("balance", ios::in | ios::binary);
inbal.read((char *) &acc, sizeof(struct MyRecord));

when I compile, it compiles nicely, but when executing I am getting the big list of error messages

./binaryfile_3[0x8048f6a]
./binaryfile_3[0x8048e38]
./binaryfile_3[0x8048e54]
/lib/libc.so.6(__libc_start_main+0xe6)[0x148d26]
./binaryfile_3[0x8048aa1]
======= Memory map: ========
00132000-002c3000 r-xp 00000000 fd:00 918500     /lib/libc-2.12.so
// continues....

although, it works in following 2 conditions.

  1. Read write are in same function
  2. If I remove the string location

I am new to c++, I tried writing using the manual and ended up in error. I searched through several solution and all end up in error while adding the string property. The above script also I grabbed from a site. I just modified it to match my issue which having different function to read/write and struct having string

Please help me to pin point the issue. Thanks.

Muneer
  • 7,384
  • 7
  • 38
  • 62
  • You cannot do a raw write of your struct if it contains a c++ objects as `string`. This works only if ste struct contains POD (plain old data) such as char, int, double, short etc. [This SO question might help](http://stackoverflow.com/questions/7046244/serializing-a-class-which-contains-a-stdstring) – Jabberwocky Apr 09 '14 at 11:30
  • I wouldn't write a structure where you have a non primitive type. String buffer is allocated on heap and what you save is just a pointer. Moreover it's C++...why strcpy? – Adriano Repetti Apr 09 '14 at 11:30
  • `string` is not a built-in type, but a class. Internally, it might have some state on the heap, pointed to by a pointer. When writing the data, that pointer will be written as is. When reading it, it will be read as is, but obviously it will point to invalid memory, resulting in undefined behavior. Which means anything can happen, although it probably won't format your hard drive. – Bart van Nierop Apr 09 '14 at 11:30
  • @MichaelWalz Oh ic! I dint know this and no guides teach me this. :) so how could I write this into file? – Muneer Apr 09 '14 at 11:31
  • @BlueBird: look at my edited comment. – Jabberwocky Apr 09 '14 at 11:32
  • @Adriano, yes man, I told in the question, I just copy the above script to test when I was running in to issue. – Muneer Apr 09 '14 at 11:32
  • @BlueBird You could look at [boost::serialization](http://www.boost.org/doc/libs/1_55_0/libs/serialization/doc/index.html) or [Cereal](http://uscilab.github.io/cereal/index.html). – Bart van Nierop Apr 09 '14 at 11:33
  • Don't use structs as file formats. Use file formats as file formats. You're introducing half a dozen unwanted dependencies even if you do it right, which you aren't here. Define yourself a file format and write yourself a library to read and write it. Or, use an existing serialization library. – user207421 Apr 09 '14 at 12:11
  • Possible duplicate of [C++ - How to write and read a structure that contain an object ? (to write and read binary)](https://stackoverflow.com/questions/19985037/c-how-to-write-and-read-a-structure-that-contain-an-object-to-write-and-r) – underscore_d Dec 15 '17 at 13:42

1 Answers1

1

Add these two functions

std::ostream & operator << ( std::ostream & os, const MyRecord & rec ) {
    os.write( rec.name, sizeof( rec.name ) );
    size_t sz = rec.location.size();
    os.write( & sz, iszeof( sz ) );
    os.write( rec.location.c_str(), sz );
    os.write( (char*) & balance, sizeof( rec.balance ) );
    os.write( (char*) & account_num, sizeof( rec.account_num ) );
    return os;
}
std::istream & operator >> ( std::ostream & is, MyRecord & rec ) {
    is.read( rec.name, sizeof( rec.name ) );
    size_t sz;
    is.read( & sz, iszeof( sz ) );
    rec.resize( sz );
    if ( sz != 0 ) {
        is.read( & rec.location[ 0 ], sz );
    }
    is.read( (char*) & balance, sizeof( rec.balance ) );
    is.read( (char*) & account_num, sizeof( rec.account_num ) );
}

And here is a usage

ofstream outbal("balance", ios::out | ios::binary);
outbal << acc;

ifstream inbal("balance", ios::in | ios::binary);
inbal >> acc;

This is poor handmade similarity of Boost.Serialization. Indeed, it would be better to use boost, then my vehicle.

borisbn
  • 4,988
  • 25
  • 42