I open two files, one input and one output. I'd like to handle exceptions for both of them, so by looking at some examples, I made this:
std::ifstream readFile;
readFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
//set the flags for stream bits that indicate failure if ON
std::ofstream writeFile;
writeFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
try{
readFile.open(inputFileName);
writeFile.open(outputFileName);
function(readFile, writeFile);
readFile.close();
writeFile.close();
}
catch(std::ifstream::failure &readErr) {
std::cerr << "\n\nException occured when reading a file\n"
<< readErr.what()
<< std::endl;
return -1;
}
catch(std::ofstream::failure &writeErr) {
std::cerr << "\n\nException occured when writing to a file\n"
<< writeErr.what()
<< std::endl;
return -1;
}
This seems like a reasonable solution, but I get a warning:
warning: exception of type 'std::ios_base::failure' will be caught [enabled by default]
catch(std::ofstream::failure &writeErr) {
^
The code does it's thing, but I'm still interested in improving my code. Where have I wronged?
No you can't. The typedef of std::ifstream::failure and std::ofstream::failure are both defined to be std::ios_base::failure.
The best thing you could do is wrap the individual calls with try-catch:
try
{
readFile.open(inputFileName);
}
catch(std::ifstream::failure &readErr)
{
}
try
{
writeFile.open(outputFileName);
}
catch(std::ofstream::failure &writeErr)
{
}
Or check the state of the streams individually in the catch block to see who failed.
The only way to handle exceptions from the two files separately would be to catch the exception and then check the failbit on the streams to determine which of them that failed:
try
{
readFile.open(inputFileName);
writeFile.open(outputFileName);
function(readFile, writeFile);
readFile.close();
writeFile.close();
}
catch (const std::ios_base::failure &err)
{
if (readFile.fail())
{
std::cerr << "\n\nException occured when reading a file\n"
<< readErr.what()
<< std::endl;
}
if (writeFile.fail())
{
std::cerr << "\n\nException occured when writing to a file\n"
<< writeErr.what()
<< std::endl;
}
return -1;
}
I was reading my C++ text and it showed me this error scaffolding. However it doesn't explain the "exception& e" part.I know that the & means something is being "passed by reference", but I've never seen the exception datatype.
#include "std_lib_facililies.h"
int main(){
try {
//your code here
keep_window_open();
return 0 ;
}
catch (exception& e) {
cerr << "error: n << e. whatO << '\n ' ;
keep_window_openO:
return 1 ;
}
catch (...) {
cerr « "Oops: unknown exception!\n";
keep_window_open() ;
return 2;
}
}
string mapFile;
cout << "Enter the file name : ";
cin >> mapFile;
ifstream mapfh;
mapfh.open(mapFile.c_str());
if(mapfh.is_open()) { ... }
else //if board file did not open properly
{
throw;
}
mapfh.close();
I am compiling with g++ in the command line. Whenever I put a file input (even with a full path i.e. /User/...etc./file.txt) it throws an error. I know the input is good, but for whatever reason the open always fails.
This isn't fully portable, but you'll get a more informed output if you interpret the errno,
#include <cerrno>
#include <cstring>
...
if(mapfh.is_open()) { ... }
else //if board file did not open properly
{
std::cout << "error: " << strerror(errno) << std::endl;
throw;
}
And if your policy is to communicate the errors as exceptions then use iostreams native support for the exceptions:
ifstream mapfh;
mapfh.exceptions(std::ios::failbit);
try {
mapfh.open(mapFile.c_str());
...
mapfh.close();
} catch (const std::exception& e) {
std::cout << e.what() << " : " << std::strerror(errno) << std::endl;
}
I have a small code with some file I/O
bool loadConfigFile(std::string configFileName)
{
std::ifstream configFile;
try
{
configFile.open(configFileName, std::ifstream::in);
if(true != configFile.good())
{
throw std::exception("Problem with config file");
}
} catch (std::exception &e)
{
fprintf(stderr, "There was an error while opening the file: %s\n %s\n" , configFileName, e.what());
configFile.close();
}
configFile.close();
return true;
}
And everytime I launch the program without the file provided as a parameter I get some rubbish on output (random characters) or an unexpected error in runtime. What am I doing wrong here ?
"%s" expects an null terminated char array as its input but the code is passing configFileName, which is a std::string. Either use std::string::.c_str() or use std::cerr instead:
std::cerr << "There was an error while opening the file: "
<< configFileName << '\n'
<< e.what() << '\n';
Note that the ifstream constructor has a variant that accepts the filename to open and the destructor will close the stream if it is open so the explicit calls to open() and close() can be omitted:
try
{
std::ifstream configFile(configFileName);
if (!configFile.is_open())
{
throw std::exception("Failed to open '" + configFileName + "'");
}
}
catch (const std::exception& e)
{
// Handle exception.
std::cerr << e.what() << '\n';
return false;
}
I've made three files using Visual C++ 2008 express for a text based RPG game. Before I really dive into the whole thing I want to get the basics ironed out: new game, save game, continue game, quit game. So far I have the make a character section (in this case find a weapon) and quit game all ironed out. I'm stuck on how to pass the weapon stats from a struct to a save file. I appear to pass the members of the struct without issue and check the file to find "junk": Ì and -858993460 in place of my values.
How should I go about fixing my save_game and continue_game functions? I've done a lot of research trying to figure this out and nothing I've tried seems to help.
Here's the important pieces of the code:
struct player_character
{
char type;
int damage;
int stability;
};
void save_game(player_character& pc, ofstream &save);
void continue_game(player_character& pc, ifstream &get);
int main()
{
player_character pc;
ofstream save;
ifstream get;
//rest of main() goes here.
//pause screen
system("pause");
return 0;
}
//rest of functions go here.
void save_game(player_character &pc, ofstream &save_data)
{
save_data.open ("save.dat", ios::binary);
if (save_data.is_open())
{
save_data << "pc.type = " << pc.type << endl;
save_data << "pc.damage = " << pc.damage << endl;
save_data << "pc.stability = " << pc.stability << endl;
//doesn't work
//save_data.write(reinterpret_cast<char*>(&pc), sizeof(pc));
save_data.close();
}
else
{
cout << " Error. Unable to open file.";
}
}
void continue_game(player_character &pc, ifstream &get_data)
{
get_data.open("save.dat");
if (get_data.is_open())
{
//doesn't work
//get.read(reinterpret_cast<char*>(&pc), sizeof(pc));
get.close();
}
else
{
cout << " Error. Unable to open file.";
}
}
Thanks for the reponse. I'm trying the following revisions. The continue_game function appears to work. I recieve no errors (yet). When I select save after making a character I recieve the following error: Unhandled exception at 0x69197a28 in Undone.exe: 0xC0000005: Access violation reading location 0xffffffcc.
Google shows it as some sort of Windows issue.
Why is my function causing this error?
void save_game(ofstream &save, player_character const &pc)
{
save.open ("save.dat");
if (save.is_open())
{
save.write(reinterpret_cast<char const *>(pc.type), sizeof pc.type);
save.write(reinterpret_cast<char const *>(pc.damage), sizeof pc.damage);
save.write(reinterpret_cast<char const *>(pc.stability), sizeof pc.stability);
save.close();
}
else
{
cout << " Error. Unable to open file.";
}
}
int continue_game(ifstream &get)
{
if (!get.read(reinterpret_cast<char *>(&pc.type), sizeof pc.type)) { /* error */ }
if (!get.read(reinterpret_cast<char *>(&pc.damage), sizeof pc.damage)) { /* error */ }
if (!get.read(reinterpret_cast<char *>(&pc.stability), sizeof pc.stability)) { /* error */ }
return pc.type;
return pc.damage;
return pc.stability;
}
I've changed the save_game and continue_game code and included it here. I also collected the debugger and autos output (small version of autos, not all seven pages). Apparently my values are not being evaluated in save_game so continue_game has nothing to work with and doesn't cause errors.
Here's the code and debugger/autos printouts:
int save_game(ofstream &save, player_character& pc)
{
save.open ("save.dat", ios::binary);
if (save.is_open())
{
//the error hits here:
save.write(reinterpret_cast<char const *>(pc.type), sizeof pc.type);
save.write(reinterpret_cast<char const *>(pc.damage), sizeof pc.damage);
save.write(reinterpret_cast<char const *>(pc.stability), sizeof pc.stability);
save.close();
}
else
{
cout << " Error. Unable to open file.";
}
return pc.type;
return pc.damage;
return pc.stability;
}
int continue_game(ifstream &get, player_character& pc)
{
get.open ("save.dat", ios::binary);
if (get.is_open())
{
if (!get.read(reinterpret_cast<char *>(&pc.type), sizeof pc.type)) { /* error */ }
if (!get.read(reinterpret_cast<char *>(&pc.damage), sizeof pc.damage)) { /* error */ }
if (!get.read(reinterpret_cast<char *>(&pc.stability), sizeof pc.stability)) { /* error */ }
get.close();
}
else
{
cout << " Error. Unable to open file.";
}
return pc.type;
return pc.damage;
return pc.stability;
}
Debugger Output Window:
First-chance exception at 0x644e7a28 in Undone.exe: 0xC0000005: Access violation reading location 0xffffffcc.
Unhandled exception at 0x644e7a28 in Undone.exe: 0xC0000005: Access violation reading location 0xffffffcc.
Autos:
- pc {type='Ì' damage=-858993460 stability=-858993460 } player_character &
type -52 'Ì' char
damage -858993460 int
stability -858993460 int
pc.type -52 'Ì' char
- save {_Filebuffer={...} } std::basic_ofstream > &
+ std::basic_ostream > {...} std::basic_ostream >
+ _Filebuffer {_Pcvt=0x00000000 _Mychar='Ì' _Wrotesome=false ...} std::basic_filebuf >
Well I'vve managed to make some progress. I discovered I needed to place arguments into my main_menu function (mind you I'm not taking about the main() function, but one I made) so that they would be passed on to my save_game function. I was also able to stop the access error by adding an & into my write function.
So this:
save.write(reinterpret_cast<char const *>(&pc.type), sizeof pc.type);
save.write(reinterpret_cast<char const *>(&pc.damage), sizeof pc.damage);
save.write(reinterpret_cast<char const *>(&pc.stability), sizeof pc.stability);
instead of:
save.write(reinterpret_cast<char const *>(pc.type), sizeof pc.type);
save.write(reinterpret_cast<char const *>(pc.damage), sizeof pc.damage);
save.write(reinterpret_cast<char const *>(pc.stability), sizeof pc.stability);
The savve_game code still doesn't work properly yet when it comes to putting data into a file but it does printout to the screen.
You have to read and write each primitive data type, one by one, and use unformatted I/O only. Example:
void serialize(std::ostream & o, Player const & p)
{
o.write(reinterpret_cast<char const *>(p.type), sizeof p.type);
o.write(reinterpret_cast<char const *>(p.damage), sizeof p.damage);
o.write(reinterpret_cast<char const *>(p.stability), sizeof p.stability);
}
Player deserialize(std::istream & i)
{
Player p;
char pt;
int pd, ps;
if (!i.read(reinterpret_cast<char *>(&pt), sizeof pt)) { /* error */ }
if (!i.read(reinterpret_cast<char *>(&pd), sizeof pd)) { /* error */ }
if (!i.read(reinterpret_cast<char *>(&ps), sizeof ps)) { /* error */ }
p.type = pt; p.damage = pd; p.stability = ps;
return p;
}
Follow-up to the previous answer and edited question.
Judging by your continue_game signature,
int continue_game(ifstream &get)
pc must be some form of global struct that holds the character you're trying to resume. If pc is allocated at run-time, then you are using it incorrectly.
When you do this:
return pc.type;
return pc.damage;
return pc.stability;
A function can only have one return-type; only pc.type would be returned. This is not the cause of the error, but rather a design flaw you should be aware of.
Your initial approach to continue_game, wherein you passed in the relevant struct as a parameter,
void continue_game(player_character &pc, ifstream &get_data)
was a much better idea. I believe that if you combine the two you will find yourself without the error.
If the error continues, please use a debugger and paste the line. In addition, I can provide a sample of the code I described if you wish me to.
It looks like I figured it out! Here's what I've come up with: struct in array...and it appears to work just fine.
void save_game(fstream& file, player_type& pc)
{
file.open("save.dat");
if (file.is_open())
for (int i = 0; i < 1; i++)
{
file << pc.w_name << endl;
file << pc.d_rate << endl;
file << pc.s_rate << endl;
file.close();
}
else
cout << " Error. Unable to open file.";
menu(pc);
}//end save function
void continue_game(fstream& file, player_type player[])
{
file.open("save.dat");
if (file.is_open())
for (int i = 0; i < 1; i++)
{
file >> player[i].w_name;
file >> player[i].d_rate;
file >> player[i].s_rate;
file.close();
}
else
cout << " Error. Unable to open file.";
for (int i = 0; i < 1; i++)
{
cout << "You are continuing the game with the following weapon: " << player[i].w_name << endl;
cout << "Which has a damage rating of " << player[i].d_rate << " and a stability rating of " << player[i].s_rate << endl;
}
cout << endl;
menu(pc);
}//end continue_game