Loading/Saving a structure using <fstream>

A

adamrobillard

Hi,

I have always used fopen and FILE* to load and save structures to file.
I am trying to convert all the older code to use proper C++ calls...
the following code works properly but I would like to know if I am
using the fstream methods properly. (as long as I am cleaning up I
might as well do it right). By the way, this is just a bogus example
with a structure, values and filename for demonstration purposes.

PS-> I have a funny feeling casting my structure to a char* is the
wrong approach! :)

#include <iostream>
#include <fstream>

struct stAnything
{
int nInteger;
long lLong;
char cChar;
double dDouble;
};

int main(void)
{
std::string sFilename = "C:/test.bin";
stAnything stSomeStructure;

stSomeStructure.nInteger = 5; // bogus values
stSomeStructure.lLong = 10;
stSomeStructure.cChar = 'A';
stSomeStructure.dDouble = 5.1;

// Save the structure to a binary file
std::eek:fstream outFile;
outFile.open(sFilename.c_str(), std::eek:fstream::eek:ut |
std::eek:fstream::binary);
if (!outFile.is_open())
{
std::cout << "Could not open the file for output." << std::endl;
return 1;
}
outFile.write((const char *) &stSomeStructure, sizeof(stAnything));
outFile.close();

// Clear the structure
stSomeStructure.nInteger = 0;
stSomeStructure.lLong = 0;
stSomeStructure.cChar = ' ';
stSomeStructure.dDouble = 0.0;

// Load the structure from a binary file
std::ifstream inFile;
inFile.open(sFilename.c_str(), std::eek:fstream::in |
std::eek:fstream::binary);
if (!inFile.is_open())
{
std::cout << "Could not open the file for input." << std::endl;
return 1;
}
inFile.read((char *) &stSomeStructure, sizeof(stAnything));
inFile.close();

// Show the loaded contents
std::cout << "{" << stSomeStructure.nInteger << ", " <<
stSomeStructure.lLong << ", " <<
stSomeStructure.cChar << ", " <<
stSomeStructure.dDouble << "} " << std::endl;
return 0;
}
 
N

Neil Cerutti

I have always used fopen and FILE* to load and save structures
to file.

The approach has the same drawbacks in C++ as it did in C. It's
not portable. It won't work at all for structs that aren't Plain
Old Data types.
I am trying to convert all the older code to use proper C++
calls...

I'm not sure it's really an improvement over the original C.

You may want to read about serialization. Reading and writing
your structs as plain old text won't be as compact, but it's
quite portable, and gives clients the benefit of human-readable
data files.
the following code works properly but I would like to know if I
am using the fstream methods properly. (as long as I am
cleaning up I might as well do it right). By the way, this is
just a bogus example with a structure, values and filename for
demonstration purposes.

PS-> I have a funny feeling casting my structure to a char* is
the wrong approach! :)

It's normally a bad idea. In this case, it's doesn't make your
code any less portable. But prefer C++ style casts intead of the
C-style cast used.
outFile.write((const char *) &stSomeStructure, sizeof(stAnything));

outFile.write(reinterpret_cast<const char *>(&stSomeStructure)
, sizeof(stAnything));
 
M

mlimber

Hi,

I have always used fopen and FILE* to load and save structures to file.
I am trying to convert all the older code to use proper C++ calls...
the following code works properly but I would like to know if I am
using the fstream methods properly. (as long as I am cleaning up I
might as well do it right). By the way, this is just a bogus example
with a structure, values and filename for demonstration purposes.

See the FAQ for more robust techniques that will work with all classes
and structs not just ones without virtual functions or, e.g.,
std::vectors:

http://www.parashift.com/c++-faq-lite/serialization.html

And check out Boost's serialization library:

http://boost.org/libs/serialization/doc/index.html
PS-> I have a funny feeling casting my structure to a char* is the
wrong approach! :)

For binary mode, you'll have to do that cast eventually.
#include <iostream>
#include <fstream>

struct stAnything
{
int nInteger;
long lLong;
char cChar;
double dDouble;
};

int main(void)

Using void like that is an "abomination"
(http://www.research.att.com/~bs/sibling_rivalry.pdf).
{
std::string sFilename = "C:/test.bin";

Should be const.
stAnything stSomeStructure;

stSomeStructure.nInteger = 5; // bogus values
stSomeStructure.lLong = 10;
stSomeStructure.cChar = 'A';
stSomeStructure.dDouble = 5.1;

// Save the structure to a binary file
std::eek:fstream outFile;
outFile.open(sFilename.c_str(), std::eek:fstream::eek:ut |
std::eek:fstream::binary);

Prefer to open and close with the constructor and destructor,
respectively, unless you need to do differently. (In this test program,
you would actually need to close the output file manually or else move
the serialization to a separate function so the file is closed via the
destructor when the function returns.)
if (!outFile.is_open())

Better would be:

if( !outFile )
{
std::cout << "Could not open the file for output." << std::endl;
return 1;
}
outFile.write((const char *) &stSomeStructure, sizeof(stAnything));

You didn't check that the write succeeded, and you should use C++-style
casts - reinterpret_cast would be appropriate here to signal a
potentially shady conversion.

Similar comments apply to the read portion.
outFile.close();

// Clear the structure
stSomeStructure.nInteger = 0;
stSomeStructure.lLong = 0;
stSomeStructure.cChar = ' ';
stSomeStructure.dDouble = 0.0;

// Load the structure from a binary file
std::ifstream inFile;
inFile.open(sFilename.c_str(), std::eek:fstream::in |
std::eek:fstream::binary);
if (!inFile.is_open())
{
std::cout << "Could not open the file for input." << std::endl;
return 1;
}
inFile.read((char *) &stSomeStructure, sizeof(stAnything));
inFile.close();

// Show the loaded contents
std::cout << "{" << stSomeStructure.nInteger << ", " <<
stSomeStructure.lLong << ", " <<
stSomeStructure.cChar << ", " <<
stSomeStructure.dDouble << "} " << std::endl;
return 0;
}

You might also be interested in this section of the FAQ:

http://www.parashift.com/c++-faq-lite/input-output.html

Cheers! --M
 
M

Marcus Kwok

mlimber said:
Prefer to open and close with the constructor and destructor,
respectively, unless you need to do differently. (In this test program,
you would actually need to close the output file manually or else move
the serialization to a separate function so the file is closed via the
destructor when the function returns.)

Alternately, he could wrap the code that uses outFile in an extra set of
{ }'s to limit its scope:

// ...
stSomeStructure.dDouble = 5.1;

{
// Save the structure to a binary file
std::eek:fstream outFile(sFilename.c_str(),
std::eek:fstream::eek:ut | std::eek:fstream::binary);
if (!outFile)
{
std::cout << "Could not open the file for output." << std::endl;
return 1;
}
outFile.write((const char *) &stSomeStructure, sizeof(stAnything));
}

// rest of code
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top