0

I'm working on a C++ beginner level project (not absolute beginner like "what's a loop" but I wouldn't say it's intermediate level either).

In this project I need to save into a file some data stored in memory in struct variables (this is plain imperative programming, with no OOP involved).

I've read a bit about options like serialization, using some non-standard libraries and such. But I need to keep it as simple and clean as possible.

So far I have 2 structs, pretty much like these:

struct client {
    string name;
    string address;
    double phone;
};
struct invoice {
    string client_name;
    double total;
};

I'm looking for something like this example provided at http://www.cplusplus.com/doc/tutorial/files:

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

int main () {
  ofstream myfile;
  myfile.open ("example.txt");
  myfile << "Writing this to a file.\n";
  myfile.close();
  return 0;
}

Is there a way to do something like that, but to write (and then be able to read) struct variables to a file, keeping it simple? Some years ago I remember handling this in a very simple way in Pascal, when writing records to files. It was something like: open file, write record field 1, write field separador, write record field 2, write field separator, write record separator. Then when reading I would search for separators. Is this not recommended in C++?

Thanks.

Floella
  • 1,279
  • 1
  • 22
  • 41
  • 2
    Yes, it can be done. Should we invent a struct, or will you show us yours? – Beta Aug 23 '15 at 16:49
  • 5
    To answer this question you *really* need to say what is in your 'struct variables'. Not all struct variables can be read or written in the same way. – john Aug 23 '15 at 16:50
  • A better starting point would be http://www.cplusplus.com/doc/tutorial/basic_io/. – R Sahu Aug 23 '15 at 16:54
  • 3
    technically you can just do `fwrite( &instanceOfStruct, sizeof(struct), 1, fileTwoWriteTo);` and read it in the same way. However you'll potentially run into endian-ness problems and data packing problems (compilers will insert pads into your structures to align them properly for performance reasons, and not all compilers do it the same way) as well as the issue that pointers won't get deep-copied so this is not an incredibly robust method. You should look into "Serialization" libraries, boost has one. Or google protocol buffers.. there are more. – RyanP Aug 23 '15 at 17:26
  • Sorry. I just added examples of the kind of struct variables I need to store in files. Thanks for the help! – Floella Aug 23 '15 at 19:48

3 Answers3

2

You could check serialization for C++, eg. Boost::serialization in the Boost library.

Is it possible to serialize and deserialize a class in C++?

Community
  • 1
  • 1
SCO
  • 1,832
  • 1
  • 24
  • 45
  • 4
    Link only answers are bad, and there are in fact more choices beyond boost serialisation. If you think the question is a duplicate, flag it as such, instead of giving an answer. – πάντα ῥεῖ Aug 23 '15 at 16:55
  • I'm was not aware of this, will try to do it next time. Looks like I should read some FAQ related to answering ! Thanks for noticing ! – SCO Aug 25 '15 at 09:28
1

You have to associate 2 functions to this class : inputStruct and OutputStruct. Input should be able to read what Output generate. The easy way consist in writing each struct element in the same order that they are implemented.

Cevik
  • 313
  • 1
  • 4
  • 17
1

The clear way to do that is implementing a serialize and deserialize function for every structure or class that you want to write to a file. You give the serialize function the reference of the output stream, and it writes each of the fields that you want to write. The deserialize do the opposite: it reads all the properties in the same order, and sets them in the current class or structure. If you use the stream operators for serialization, the output file will be a text file.

With c++ you can overload stream operator, so it will look pretty in your code, but in other languages you must use functions for that.

You can also use binary serialization, but it is more problematic, because you need to check the endianness of the platform that you use currently. If you will just use it on one platform, you can try write, and read functions. They need the pointer of the variable, and the size, and they copy them into/from the file. Use these for every property separate, never copy whole structures, because it can lead to errors easily.

UPDATE:

I made serialize and deserialize functions. I haven't tested them, so im not 100% sure they will work.

Static functions:

void serialize_client( ofstream& out, client& cl )
{
    out << cl.name << endl;
    out << cl.address << endl;
    out << cl.phone << endl;
}

void deserialize_client( ifstream& in, client& cl )
{
    getline( in, cl.name );
    getline( in, cl.address );
    in >> cl.phone;    
}

Usage:

client client_instance;
deserialize_client( cin, client_instance );   
serialize_client( cout,  client_instance );

With operator overload:

ostream& operator<<( ostream& os, const client& cl )
{
    os << cl.name << endl;
    os << cl.address << endl;
    os << cl.phone << endl;
    return os;
}

istream& operator>>( istream& is, client& cl )
{
    getlise( is, cl.name );
    getlise( is, cl.address );
    is >> cl.phone; 
    return is;
}

client client_instance;
cin >> client_instance;   
cout << client_instance;
  • Thanks for the info :) However, data is not going to be sent over a network (this is a desktop program that only runs on one computer). I do need the program to run on different computers, though. Would I still need to deal with serialization in that case? – Floella Aug 23 '15 at 20:35
  • 1
    You can serialize data into a text file, in that case you won't have problems with endianness, but it uses more storage. If you serialize into a binary file you must deal with endianness. – László Kardinál Aug 24 '15 at 00:17
  • Thanks :) I'd rather use more storage than deal with endianness. About your code: where would I set the file name if I choose the static functions (no operator overload)? Should I declare `std::ifstream in` and `std::ofstream out` objects before calling the functions? Also, I'm a bit confused as to why you pass cin and cout as arguments when calling the functions. Thanks again for all the help :) – Floella Aug 25 '15 at 12:23
  • 1
    Both cin, and ifstream are istreams, and both cout and ofstream are ostreams. (istream and ostream are the superclasses of these classes) So if you use istream in your your code, you can use cin as and istream, and the same with ifstream. You can simply change cin to the ifstream of your file, and the same with the cout. – László Kardinál Aug 25 '15 at 14:41