Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Error with ifstream and exceptions

Reply
Thread Tools

Error with ifstream and exceptions

 
 
Goran
Guest
Posts: n/a
 
      02-08-2011
On Feb 8, 2:03*am, James Kanze <(E-Mail Removed)> wrote:
> Just for the record, well written C++ programs don't use
> exceptions for reporting an error when opening a file.


I disagree. It all depends on what needs to happen on such an error.

Here's an example using MFC (much hated and IMO not all that good, but
a very well established library):

CWinApp theApp;

void Process(const CString& s)
{
// Process baby, process.
}

void Delete(CException* p) { p->Delete(); }

int main(int, char*[])
{
try
{
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(),
0))
AfxThrowResourceException();

CString s;
CStdioFile f("file.txt", CFile::modeRead).
while (f.ReadString(s))
Process(s);
}
catch(const CException* e)
{ // exception-safety elided for brevity.
e->ReportError();
e->Delete();
}
return 0;
}

Here, if there is a problem in accessing a file, program ends up with
"file.txt was not found", or, "Encountered a sharing violation while
accessing file.txt", or similar, on screen. That's not bad, not bad at
all.

It should be noted that, error-reporting wise, exception thrown from
CFile/CStdioFile constructor will be better than something __anyone
will come up with in 3 minutes of coding__. Why? Because failure modes
of this small piece are already many, and a library worth it's salt
__should__ emit errors containing a rather detailed error info for all
that (and amazingly, MFC does it).

In this particular case, exception thrown will be CFileException*,
containing "exception cause" number, system error number and a file
name. And, of course, exception object is capable of turning it's
contents into localized an error text, and a localized one at that.

And there's something else: some people argue that program should
offer a better user-friendly error explanation instead of saying
exactly what happened. Yes, but that is __hard__. That is hard,
because code needs to know up-front about a likely failure reason, and
it needs to check whether actual failure indeed was caused by that,
and only in that case a better, user-friendly message is OK (and even
then, original error cause should probably remain). Otherwise said
message might be completely unhelpful, because guess was wrong and
real failure cause was omitted. I think we have all seen this in the
wild, numerous times.

tl;dr: if error info, both "programmatic" and "user-friendly", is
competent, there's nothing wrong in using exceptions even for resource
(like a file object) creation problems. IMO, your idea that you need
error-return for that is caused more by incompetent error reporting in
existing libraries, than by some inherent problem in handling such
failures with exceptions.

Goran.
 
Reply With Quote
 
 
 
 
Paul
Guest
Posts: n/a
 
      02-08-2011

"James Kanze" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> On Feb 8, 12:54 pm, Marco <(E-Mail Removed)> wrote:
>> On 2011-02-07 James Kanze <(E-Mail Removed)> wrote:
>> > > in the following code I get an error message after the
>> > > file is output. What did I do wrong?

>
>> > Basically, I think you've misunderstood how IO works.

>
>> That's possible. C++ is new to me.

>
>> > > try {
>> > > fileIn.open( "file" ); }
>> > > catch ( ifstream::failure e ) {
>> > > cout << "Error."; }

>
>> > > string line;
>> > > while ( getline( fileIn, line ) ) {

>
>> > And when you've no more lines to read?

>
>> Then I expect this loop to end, and it does so as far as I tested.

>
> Yes, but why? That's the heart of understanding C++ IO. It
> ends because a call to getline fails, because there is no line
> to get.
>
> Basically, an istream maintains three "status" bits: badbit,
> failbit and eofbit. In practice (regretfully), you can almost
> forget about badbit, since most implementations will never set
> it. (The intent is that it be set in cases of a "hard" error,
> such as a read error on disk. In practice, most, if not all
> implementations just treat this as if it were end of file.) The
> most important status bit, in the first instance, is failbit; it
> is set if the preceding read failed, either because there was no
> data to read (end of file), or because the input had the wrong
> format (impossible with getline, but if the input stream has
> something like "abc" when you try to read an int).


I cannot ignore this inconsistency, have you taken any medication today
James?
I refer to your other post in this thread re:

"In practice, the only thing you'd ever use exceptions for with
IO is badbit, which should be set in case of a hardware problem
(disk read error, etc.)."
and
"I've never seen an example which triggers an exception on
failbit."

What the hell man, this seems to define a whole new level of inconsistency.
10 out of 10 for effort though as it also seems to define a new level of
long postings.

<snip rest>

 
Reply With Quote
 
 
 
 
Paul
Guest
Posts: n/a
 
      02-08-2011

"Leigh Johnston" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> On 08/02/2011 17:19, Pete Becker wrote:
>> On 2011-02-08 11:45:45 -0500, Leigh Johnston said:


<snip>
I thought Leigh's post was proving the last line would be read even if it
didn't end in newline.
I didn't realise it was to prove about failbit either.

 
Reply With Quote
 
Marco
Guest
Posts: n/a
 
      02-08-2011
On 2011-02-08 James Kanze <(E-Mail Removed)> wrote:

> [...]


Thanks for the explanations about the bits.

> while ( getline( fileIn, line ) || fileIn.gcount() != 0 ) {
> if ( !fileIn ) {
> // incomplete last line...
> }
> // ...
> }


I don't see the need to write this. I always get the last line.
// incomplete last line...
never gets executed. If I understand you correctly this line should be
executed for instance if I read in a file that just contains »hello«. The
getline fails because eof is seen. But getline sees the »hello« with my
construct without gcount().

> You didn't. Obviously, opinions as to when to use exceptions
> vary, but in fact, my response was very oriented to you, the
> context of your posting, and you're apparent programming level
> in C++; Ian probably summed it up best with his response to me:
> you don't use exceptions for a missing file unless the fact that
> it is missing is somehow exceptional. (Which of course begs the
> point as to what is exceptional, but in a short program which
> reads a more or less random file, the fact that the file isn't
> there could hardly be considered exceptional.)


I understand.

> I'd write something like:
>
> std::ifstream fileIn( name );
> if ( !fileIn.open() )
> throw ...


As I wrote before: if(!fileIn.open()) does not work, since open() has a void
return type and it cannot be converted to a bool. I can write

fileIn.open("file");
if (!fileIn) ... // This works

>> return -1;

>
> The choice of -1 probably isn't optimal here. The only value
> guaranteed to be treated as an error is EXIT_FAILURE;


Thans for pointing this out.

> It's fine; my comments above are really nits (except maybe for
> the case where the file doesn't end with a new line---if you're
> on Windows, try entering "abc", and nothing else, in Notepad,
> then save it and use it to test your program).


I don't use windows, I used another editor. But I was not able to trigger the
problem you addressed. Maybe I did something wrong. Just writing »Hello« to a
file shouldn't result in a new line character, should it?

> Another improvement that you'd want to make in production code:
> before returning 0:
> cout.flush();
> if ( !cout )
> // Write error in the output...
> This is more important than closing the input (here, since the
> input will be automatically closed when fileIn goes out of
> scope).


I read everywhere that this is not necessary, indeed but one should do it
nonetheless. I put it, so it's clear when the file is closed.

> If for some reason (e.g. disk full) there has been
> a write error, the output data will not be complete, and your
> user should definitely be informed.


cout.flush() writes everything what is left in the buffer to the screen (or to
a file when the output is redirected) if I'm not mistaken. But what does !cout
mean, that cout fails? But how can I inform the user if I cannot do a cout?
When should cout fail? cout writes to the screen, not to the disk.


Regards
Marco

 
Reply With Quote
 
Ian Collins
Guest
Posts: n/a
 
      02-08-2011
On 02/ 9/11 06:22 AM, Leigh Johnston wrote:
>
> My original assertion included a code snippet with test data and results
> which should have made it obvious I was referring to failbit behaviour
> (look at the bloody while statement).


A clearer example would have been

#include <iostream>
#include <fstream>

int main()
{
std::ifstream f("y");
std::string s;
getline(f, s);
getline(f, s);
std::cout << f.fail() << ' ' << f.eof() << std::endl;
}

Which removes the third call to getline.

--
Ian Collins
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      02-08-2011
On Feb 8, 4:04 pm, Leigh Johnston <(E-Mail Removed)> wrote:
> On 08/02/2011 15:37, James Kanze wrote:


> > Note that with getline on a string, there are only two possible
> > causes of error (beyond hardware read errors): you run out of
> > memory, or you reach end of file without seeing a new line, but
> > after having seen at least one character. In the first case,
> > badbit should be set (but it wouldn't surprise me if some
> > implementations let the bad_alloc exception leak out). In the
> > second, failbit gets set, but of course, end of file has been
> > seen internally, so eofbit is also set. Typically, this will
> > result in the last, incomplete line not being seen by the
> > program (and this is what will happen with your code if the
> > input file doesn't end with a newline character). If this is
> > not the behavior you want, then you need something like:


> > while ( getline( fileIn, line ) || fileIn.gcount() != 0 ) {
> > if ( !fileIn ) {
> > // incomplete last line...
> > }
> > // ...
> > }


> > Depending on context and what you are doing, this may or may not
> > be important.


> If VC++ and g++ are behaving correctly then what you are asserting is
> plain false. Given the following program:


> int main()
> {
> std::ifstream f("c:\\tmp\\test.dat");
> std::string s;
> while(getline(f, s))
> std::cout << "[" << s << "]\n";
> }


> The output is
> [one]
> [two]


> Given the input file test.dat which contains:


> (VC++, Windows)
> 00000000 6f 6e 65 0d 0a 74 77 6f |one..two|
> (g++, Linux)
> 00000000 6f 6e 65 0a 74 77 6f |one.two|


In which case, I'm clearly mixing it up with something else.
(I'm not too sure how VC++ maps the input under Windows, but
I am sure that g++ under Linux treats a single 0a as a line
ending, and not a line separator.)

Checking in the standard: my description applies to some of the
other unformatted input functions, but not to getline. So the
correct loop for getline would be:

while ( getline( fileIn, line ) ) {
if ( fileIn.eof() ) {
// incomplete last line...
}
// ...
}

My bad. Sorry for the confusion.

--
James Kanze
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      02-08-2011
On Feb 8, 6:47 pm, Marco <(E-Mail Removed)> wrote:
> On 2011-02-08 James Kanze <(E-Mail Removed)> wrote:


> > [...]


> Thanks for the explanations about the bits.


> > while ( getline( fileIn, line ) || fileIn.gcount() != 0 ) {
> > if ( !fileIn ) {
> > // incomplete last line...
> > }
> > // ...
> > }


> I don't see the need to write this. I always get the last line.
> // incomplete last line...
> never gets executed. If I understand you correctly this line should be
> executed for instance if I read in a file that just contains hello. The
> getline fails because eof is seen. But getline sees the hello with my
> construct without gcount().


That was my mistake. I was thinking of some other unformatted
read functions. This is one of the rare cases where it might
make sense to test for eof after a successful read, e.g.:

while ( getline( fileIn, line ) ) {
if ( fileIn.eof() ) {
// incomplete last line...
}
// ...
}

[...]
> > I'd write something like:

>
> > std::ifstream fileIn( name );
> > if ( !fileIn.open() )
> > throw ...


> As I wrote before: if(!fileIn.open()) does not work, since
> open() has a void return type and it cannot be converted to
> a bool. I can write


Sorry. That's a typo. By passing the filename to the
constructor, open has already been called, and the test should
use is_open():

if ( !fileIn.is_open() ) ...

> fileIn.open("file");
> if (!fileIn) ... // This works


Also. In this particular case, I prefer the is_open, since it's
more explicit with regards to what I'm testing, but behind the
scenes:
if ( !fileIn.open() )
if ( fileIn.fail() )
if ( !fileIn )
all do exactly the same thing. (Immediately after an attempt to
open a file, at any rate. Before an attempt to open a file, the
last to will show success, although there is no file open, and
after other operations on an already open file, the last two
could show failure, although the file is open.)

[...]
> I don't use windows, I used another editor. But I was not able
> to trigger the problem you addressed. Maybe I did something
> wrong. Just writing Hello to a file shouldn't result in
> a new line character, should it?


I was wrong, see above. But with regards to just writing
"hello" to a file, it all depends. Unix is very line oriented
in its text handling, and the editor I normally use (vim, both
in Windows and in Unix) is incapable of writing a file without
a final newline.

> > Another improvement that you'd want to make in production code:
> > before returning 0:
> > cout.flush();
> > if ( !cout )
> > // Write error in the output...
> > This is more important than closing the input (here, since the
> > input will be automatically closed when fileIn goes out of
> > scope).


> I read everywhere that this is not necessary, indeed but one
> should do it nonetheless. I put it, so it's clear when the
> file is closed.


For input, it's not too critical.

> > If for some reason (e.g. disk full) there has been
> > a write error, the output data will not be complete, and your
> > user should definitely be informed.


> cout.flush() writes everything what is left in the buffer to
> the screen (or to a file when the output is redirected) if I'm
> not mistaken.


That's a good first approximation.

> But what does !cout mean,


if ( !cout )
is just the opposite of
if ( cout )
Streams are designed to convert implicitly and support operators
so that they can be used more or less as boolean values, at
least in if's and such. If the result of the conversion is
considered true, all preceding io has succeeded. If the result
is false, an "error" has occured.

> that cout fails?


That depends on the system. The most frequent case is when the
output is going to disk, and the disk if full.

> But
> how can I inform the user if I cannot do a cout?


That's what cerr is for. And the final return status. Under
Unix, one common idiom is:
prog input > tmp && mv tmp input
prog (your program) reads input, and writes the transformed data
to a temporary file. If prog succeeds (returns 0 or
EXIT_SUCCESS), you then replace your original file with the
output. But only if prog succeeds; you don't want to replace
the original file if you've lost half of it, because the disk
was full.

> When should cout fail?


Disk full is probably the most frequent case. Or a loss of
connection to the machine on which you were working. Or
whatever. cout can go pretty much anywhere, and there are all
sorts of possible failures.

> cout writes to the screen, not to the disk.


Cout writes to where ever the OS wants it to. And the OS's
I use allow you to choose between the screen, a file, a pipe to
another process, and a few other odds and ends. If you're
writing to the screen, when working on a remote machine, and
loose the connection, it's generally not important. If you're
writing to a pipe, and the reader disappears, no problem either.
But if you're writing to disk... See above.

--
James Kanze
 
Reply With Quote
 
Marco
Guest
Posts: n/a
 
      02-08-2011
On 2011-02-08 James Kanze <(E-Mail Removed)> wrote:

> On Feb 8, 6:47 pm, Marco <(E-Mail Removed)> wrote:
> > On 2011-02-08 James Kanze <(E-Mail Removed)> wrote:

>
> > > [...]

>
> > Thanks for the explanations about the bits.

>
> > > while ( getline( fileIn, line ) || fileIn.gcount() != 0 ) {
> > > if ( !fileIn ) {
> > > // incomplete last line...
> > > }
> > > // ...
> > > }

>
> > I don't see the need to write this. I always get the last line.
> > // incomplete last line...
> > never gets executed. If I understand you correctly this line should be
> > executed for instance if I read in a file that just contains »hello«. The
> > getline fails because eof is seen. But getline sees the »hello« with my
> > construct without gcount().

>
> That was my mistake.


No problem.

> > As I wrote before: if(!fileIn.open()) does not work, since open() has a
> > void return type and it cannot be converted to a bool. I can write

>
> Sorry. That's a typo.


> if ( !fileIn.is_open() ) ...


That works.

> Also. In this particular case, I prefer the is_open, since it's more
> explicit with regards to what I'm testing, but behind the scenes:
> if ( !fileIn.open() )
> if ( fileIn.fail() )
> if ( !fileIn )
> all do exactly the same thing.


Same typo again? Nevermind.

> > I don't use windows, I used another editor. But I was not able
> > to trigger the problem you addressed. Maybe I did something
> > wrong. Just writing »Hello« to a file shouldn't result in
> > a new line character, should it?

>
> I was wrong, see above.


No problem.


> But with regards to just writing "hello" to a file, it all depends. Unix
> is very line oriented in its text handling, and the editor I normally use
> (vim, both in Windows and in Unix) is incapable of writing a file without a
> final newline.


I use vim, too. That vim isn't capable of writing text files without final
newline makes me think of the reason. In general vim isn't wrong. Maybe a text
file has to have a final newline by definition. But that's just a guess, I
don't know.

> > that cout fails?

>
> That depends on the system. The most frequent case is when the
> output is going to disk, and the disk if full.


That's what I also thought of in the first place, output redirection.

> > But how can I inform the user if I cannot do a cout?

>
> That's what cerr is for.


Right.

> And the final return status.


True. I completely forgot about this, my fault.

> > When should cout fail?

>
> > cout writes to the screen, not to the disk.

>
> Cout writes to where ever the OS wants it to. And the OS's
> I use allow you to choose between the screen, a file, a pipe to
> another process, and a few other odds and ends. If you're
> writing to the screen, when working on a remote machine, and
> loose the connection, it's generally not important. If you're
> writing to a pipe, and the reader disappears, no problem either.
> But if you're writing to disk... See above.


Of course, I just thought about the default case.


Regards
Marco

 
Reply With Quote
 
Bo Persson
Guest
Posts: n/a
 
      02-08-2011
James Kanze wrote:
> On Feb 7, 8:46 pm, "Bo Persson" <(E-Mail Removed)> wrote:
>> Marco wrote:

>
> [...]
>>> I mean to switch the exceptions off again after having switched
>>> them on before to avoid that the while loop is able to throw an
>>> exception?

>
>> But here the open() functions is not likely to throw any
>> exceptions of the kind you try to catch. Open() returns NULL if it
>> cannot open the file (which is not considered exceptional).

>
> The open function also sets failbit if it fails to open the
> file, and he's explicitly asked for an exception if failbit is
> set.


Yes, I was confusing it with basic_filebuf:pen, which returns a
pointer.

Still, it seems a bit excessive to enable exceptions just to catch the
one possibly thrown from fstream:pen, instead of just calling
is_open to see if the open attempt was succesful.



Bo Persson


 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      02-09-2011
On Feb 8, 9:16 pm, "Bo Persson" <(E-Mail Removed)> wrote:
> James Kanze wrote:
> > On Feb 7, 8:46 pm, "Bo Persson" <(E-Mail Removed)> wrote:
> >> Marco wrote:


> > [...]
> >>> I mean to switch the exceptions off again after having switched
> >>> them on before to avoid that the while loop is able to throw an
> >>> exception?


> >> But here the open() functions is not likely to throw any
> >> exceptions of the kind you try to catch. Open() returns NULL if it
> >> cannot open the file (which is not considered exceptional).


> > The open function also sets failbit if it fails to open the
> > file, and he's explicitly asked for an exception if failbit is
> > set.


> Yes, I was confusing it with basic_filebuf:pen, which returns a
> pointer.


> Still, it seems a bit excessive to enable exceptions just to catch the
> one possibly thrown from fstream:pen, instead of just calling
> is_open to see if the open attempt was succesful.


Agreed. If you want an exception, just write:
if ( !file.is_open() )
throw ...
after the open.

Part of the problem is that the granularity in the error types
is not fine enough. failbit can mean too many different things
for you to want to convert them all to exceptions. Depending on
the situation, you might want to throw if you can't open a file,
or if there is a format error in the file, but you almost never
want an exception to indicate that you've finished reading the
file.

--
James Kanze
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Exceptions - How do you make it work like built-in exceptions? Lie Python 3 01-14-2008 06:45 PM
Exceptions + Performance on path without exceptions gratch06@gmail.com C++ 3 04-16-2007 08:52 PM
Checked exceptions vs unchecked exceptions Ahmed Moustafa Java 5 07-14-2004 01:46 PM
Custom exceptions -- inherit from exceptions.Exception? Paul Miller Python 3 11-12-2003 09:24 AM
error passing ifstream. why? Giulio C++ 2 06-24-2003 09:19 PM



Advertisments