Saving objects to binary file with some caveats?

I have a project in my programming class that involves saving an object to a binary file. The class contains two string variables and an int.

So, I would normally write the strings to the file along with their length and of course also save the int variable, but my professor doesn't want it done this way.

He has these requirements:
-The object must be written AS A WHOLE to the file, meaning you can't just pass all three variables to the file
-The strings cannot at any time be converted to c-strings, so no use of c_str()
-Coded for Linux systems

How would one go about doing this? All the answers I've seen online serialize the data of an object in separate chunks, but he wants it saved all as a whole.

Any help would be much appreciated!
Last edited on
Strange requirements. I don't think it's possible. std::string contains a pointer to the data stored elsewhere so you can't just save the object that contains the string because that would not save the actual characters.
I was thinking the same thing!
Both him and the SI for the class have said otherwise, however. The SI told me "If you could determine the size of the two string objects in the Song object, you may be able to write the entire Song object to the file at once." But just because I know the size of the strings doesn't help much in my eyes, because like you said, when I write the object to the file it'll just write the pointer, not the actual string.
There is something called "small string optimization" that most modern implementations of std::string uses. It means that character data for short strings are stored inside the string object itself. If the string is short enough to use the small string optimization you might be able save the whole string in one go, but this will only work on implementations where the "small string" doesn't use a pointer (I think this rules out GCC's implementation). It seems strange if this is what they want you to take advantage of? What do you do for long strings? And are you really supposed to rely on non-portable implementation details of std::string? I wonder if there is some misunderstanding somewhere...
Last edited on
if you are allowed to trunc the strings to a max size, its possible at the 'practical' level but convoluted.
you can make a 'serialize me' class that repackages the data in the other class and can be read and written directly, and the other class will use this one in its read/write.

eg
profclass.writetofile()
is going look like
serialclass x = *this;
file.write(serialclass);

and the load will be similar.

but if you cannot cap the max string length, its not possible.
you dont need c_str (you can use &string[0] as a surrogate)
but you do have to copy it to a local array of a max size -- that array would be best to be a c-string but it does not have to be, it can be anything, its raw bytes at this level, it could be an int array :P

he may come back to say that writing a different class instead of the main one is cheating, but, polymorphism is a thing. You can have a nice discussion on semantics and the letter of the law or something.
Last edited on
you do have to copy it to a local array of a max size -- that array would be best to be a c-string but it does not have to be, it can be anything, its raw bytes at this level, it could be an int array :P

If it's not null-terminated it's not a C-string (I guess). So leaving the rest of the data uninitialized and instead using an integer variable to keep track of the length might be OK.

he may come back to say that writing a different class instead of the main one is cheating

If that is an issue you could just implement the original class this way.
Last edited on
I wonder what type of class this is? Embedded? Hacking?

This is Fundamentals of CS II!

So, using strings is extra credit, the normal way is to use a c-string with a max length of 256. I think that if you were to implement it with strings, he doesn't want a max size. I'm definitely going to ask for a better explanation of how to do it with strings, because the solutions you guys have found seem way too convoluted and impractical to be what he's expecting.

Thanks so much for the help though y'all!
yea the ultimate cheeze here would be to use pascal strings where the first N bytes are the length that will follow, the opposite approach to C.
I mean, at some point, theres a limit. even if its an 8 or 10 byte string length... which will fill the disk in short order (fix, in windows at least, write to a compressed folder and zero out the unused bytes :P)
Last edited on
You could write the data to a buffer and then write it to the file in one shot.

Although the requirement may sound strange, there are times when you need to do this. We have just such a problem at work. The reader and the writer are two different programs. The reader waits for data to be available at the end of the file and then reads it. If the data isn't a full record then the reader could fail with EOF. So the writer has to use the read() system call to ensure that the data will appear to arrive all at once.

At least I think that's what happens. It's been a while.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <iostream>
#include<fstream>
#include <string>
#include <cstring>

using std::string;
using std::cout;
using std::ifstream;
using std::ofstream;
using std::istream;
using std::ostream;
using std::memcpy;

class MyClass {
public:
    string str;
    int i;
    void read(istream&);
    void write(ostream &);
};


void MyClass::write(ostream &os)
{
    // This writes i, then the size of the string, then the string itself
    
    size_t bufsize = (sizeof(i) // i
		      + sizeof(size_t) // size of string
		      + str.size());
    char *buf = new char[bufsize];
    char *dst = buf;

    memcpy(dst, &i, sizeof(i));    // copy i to buf
    dst += sizeof(i);		   // advance dst pointer

    size_t sz = str.size();	// copy size of str to buf and advance pointer
    memcpy(dst, &sz, sizeof(sz));
    dst += sizeof(sz);
    
    
    if (str.size()) {
	memcpy(dst, &str[0], str.size());
	dst += str.size();
    }

    os.write(buf, bufsize);
    delete[] buf;
}


void MyClass::read(istream &is)
{
    is.read((char*)&i, sizeof(i));
    size_t sz;
    is.read((char*)&sz, sizeof(sz));

    char *buf = new char[sz];
    is.read(buf, sz);
    str.assign(buf, sz);
    delete[] buf;
}


int main()
{
    MyClass mine, yours;
    mine.i = 1234;
    mine.str = "Hello world";

    ofstream outfile("myfile.bin");
    mine.write(outfile);
    outfile.close();

    ifstream infile("myfile.bin");
    yours.read(infile);
    cout << yours.i << ' ' << yours.str << '\n';
}
Last edited on
Topic archived. No new replies allowed.