When operator>> can throw...

D

Daniel T.

This is basically what I have now...

class Foo { /* definition irrelevant */ };

istream& operator>>( istream& is, Foo& foo ); // could throw

int main() {
ifstream file( "file.txt" );
Foo foo;
int i = 0;
try {
while ( file >> foo ) {
//process foo;
++i;
}
}
catch ( exception& e ) {
cout << "error at: " << i << '\n';
}
}

The problem I'm having is that op>> could throw. If it does, I don't
want to process the foo object that threw, but I want the program to
report an error and continue extracting foo objects.

I could make op>> a no-throw and give Foo some sort of error or invalid
state, but that doesn't feel right. I can't report the error inside the
op>> because I don't have enough information to make a full report.

Does anyone have a better idea, or do I give Foo and error state and
query it after the read?
 
E

Erik Wikström

This is basically what I have now...

class Foo { /* definition irrelevant */ };

istream& operator>>( istream& is, Foo& foo ); // could throw

int main() {
ifstream file( "file.txt" );
Foo foo;
int i = 0;
try {
while ( file >> foo ) {
//process foo;
++i;
}
}
catch ( exception& e ) {
cout << "error at: " << i << '\n';
}
}

The problem I'm having is that op>> could throw. If it does, I don't
want to process the foo object that threw, but I want the program to
report an error and continue extracting foo objects.

I could make op>> a no-throw and give Foo some sort of error or invalid
state, but that doesn't feel right. I can't report the error inside the
op>> because I don't have enough information to make a full report.

Does anyone have a better idea, or do I give Foo and error state and
query it after the read?

Something like this perhaps?

int main() {
ifstream file( "file.txt" );
Foo foo;
int i = 0;
while ( file.eof() == false ) {
try {
file >> foo;
} catch ( exception& e )
cout << "error at: " << i << '\n';
continue;
}
++i;
}
}
 
D

Daniel T.

Erik Wikström said:
On 2008-01-19 16:15, Daniel T. wrote:

Something like this perhaps?

int main() {
ifstream file( "file.txt" );
Foo foo;
int i = 0;
while ( file.eof() == false ) {
try {
file >> foo;
} catch ( exception& e )
cout << "error at: " << i << '\n';
continue;
}
++i;
}
}

That doesn't handle the end of file correctly...

I could do something like:

typedef int Foo;

int main() {
ifstream file( "file.txt" );
Foo foo;
int i = 0;
while ( file.eof() == false ) {
try {
while ( file >> foo ) {
// process foo
++i;
}
} catch ( exception& e ) {
cout << "error at: " << i << '\n';
}
}
}

But that seems strange.

Does anyone have a different idea?
 
J

Jerry Coffin

That doesn't handle the end of file correctly...

I could do something like:

typedef int Foo;

int main() {
ifstream file( "file.txt" );
Foo foo;
int i = 0;
while ( file.eof() == false ) {
try {
while ( file >> foo ) {
// process foo
++i;
}
} catch ( exception& e ) {
cout << "error at: " << i << '\n';
}
}
}

But that seems strange.

Does anyone have a different idea?

I don't really _like_ it, but this seems at least a bit less convoluted:

int i=1;
for(;;) {
try {
in>>foo;
if (!in)
break;
// process foo
++i;
}
catch(exception const &) {
std::cout << "error at: " << i << "\n";
}
}

I think the better solution would be for a failure to read an object
correctly to set the stream's fail bit to indicate the problem.
Processing this cleanly still isn't entirely trivial, but it's quite a
bit cleaner:

int i = 1;
do {
in.clear();
while (in>>f) {
foos.push_back(f);
++i;
}
if (!in.eof())
std::cout << "Error at: " << i << "\n";
} while (!in.eof());

If you want to produce robust code, something along this general line
(i.e. checking for what went wrong when a stream is no longer good() and
reacting accordingly) is often necessary anyway. Of course, "reacting
accordingly" normally involves more than a completely cryptic error
message, but we'll leave that alone for now...

I think you could make a fair argument that errors in input are to be
expected sufficiently often that throwing an exception from operator>>
is never really justified.
 
J

James Kanze

Daniel said:
This is basically what I have now...
class Foo { /* definition irrelevant */ };
istream& operator>>( istream& is, Foo& foo ); // could throw
int main() {
ifstream file( "file.txt" );
Foo foo;
int i = 0;
try {
while ( file >> foo ) {
//process foo;
++i;
}
}
catch ( exception& e ) {
cout << "error at: " << i << '\n';
}
}
The problem I'm having is that op>> could throw.

The question is: what can it throw, and do you want to handle
it? A well written >> will probably only throw something like
bad_alloc, which you generally cannot really handle anyway.
If it does, I don't want to process the foo object that threw,
but I want the program to report an error and continue
extracting foo objects.

Doesn't that depend on why it threw?
I could make op>> a no-throw and give Foo some sort of error
or invalid state, but that doesn't feel right. I can't report
the error inside the op>> because I don't have enough
information to make a full report.

I'm not sure I understand. Error reporting for istream's is
pretty much standardized: if you encounter a format error, or
there is nothing left to read, you set failbit, a hard read
error, badbit, and while not really an error, anytime you see
EOF, you should set eofbit.

After that, it is the users responsibility to determine whether
he wants that error to throw or no. It's sometimes (often?)
appropriate in the case of badbit, only rarely for failbit, and
probably never for eofbit.

Note that recovering from the error can be more or less
difficult: you have to clear the error in the stream, of course,
but you also have to resynchronize. (For line oriented input,
where each record is a line, it is usual to use getline(), and
then use an istringstream to parse the text. This automatically
leaves the stream "synchronized" for the next input.)
Does anyone have a better idea, or do I give Foo and error
state and query it after the read?

I doubt that Foo should have error state, but istream already
has it.
 
J

James Kanze

Something like this perhaps?
int main() {
ifstream file( "file.txt" );
Foo foo;
int i = 0;
while ( file.eof() == false ) {
try {
file >> foo;
} catch ( exception& e )
cout << "error at: " << i << '\n';
continue;
}
++i;
}
}

That's almost certainly wrong. It doesn't handle EOF correctly.
(Of course, his original code didn't handle EOF at all.)
 
D

Daniel T.

James Kanze said:
Daniel T. wrote:





The question is: what can it throw, and do you want to handle
it? A well written >> will probably only throw something like
bad_alloc, which you generally cannot really handle anyway.

I took the above advice (and Jerry Coffin's) and redesigned my op>> so
that it will not throw.
I'm not sure I understand. Error reporting for istream's is
pretty much standardized: if you encounter a format error, or
there is nothing left to read, you set failbit, a hard read
error, badbit, and while not really an error, anytime you see
EOF, you should set eofbit.

I just read
http://www.unc.edu/depts/case/pgi/pgC++_lib/stdlibug/err_7848.htm and it
seems to indicate that I should not set badbit inside my op>>, rather
that is something the stream itself would do if there is a problem
internal to it.

(from the website cited.)
The flag ios_base::badbit indicates problems with the underlying
stream buffer. These problems could be:
* Memory shortage. There is no memory available to create the
buffer, or the buffer has size zero for other reasons[21], or the
stream cannot allocate memory for its own internal data, as with
iword and pword.
* The underlying stream buffer throws an exception. The stream
buffer might lose its integrity, as in memory shortage, or code
conversion failure, or an unrecoverable read error from the
external device. The stream buffer can indicate this loss of
integrity by throwing an exception, which is caught by the stream
and results in setting the badbit in the stream's state.
 
J

Jerry Coffin

[ ... ]
I just read
http://www.unc.edu/depts/case/pgi/pgC++_lib/stdlibug/err_7848.htm and it
seems to indicate that I should not set badbit inside my op>>, rather
that is something the stream itself would do if there is a problem
internal to it.

True -- badbit normally indicates something severely wrong with the
stream itself, such an unrecoverable error attempting to read from the
disk or something like that.

You want to use failbit to report a failed conversion. That's typically
set by an operator>>, or something it delegates to such as the num_get
in the locale associated with that stream.
 
J

James Kanze

I just read
http://www.unc.edu/depts/case/pgi/pgC++_lib/stdlibug/err_7848.htmand
it seems to indicate that I should not set badbit inside my
op>>, rather that is something the stream itself would do if
there is a problem internal to it.

This depends on how you implement your operator>>. There are
two basic approches:

-- You implement your operator>> in terms of other, existing
operator>> (and other istream functions, like get() or
peek()). In this case, you probably shouldn't bother with
badbit, since it will be (or should be, at least) fully
handled by the operator>> functions you call. The same
thing holds for eofbit---the only error bit you should be
concerned with if failbit, because that is the indicator
that either no data was present (if eof was encountered), or
that there was an error in the format (no eof).

-- You implement your operator>> by means of direct access to
the streambuf, reading individual characters. In that case,
you should catch any exception and map it to badbit; you
also have to ensure that anytime you see EOF (including
during look-ahead), you set eofbit, and that you never try
to read the streambuf once eofbit has been set. (I usually
use a small reader class to handle the the eofbit stuff.)

The text you quote seems to only consider the first case. While
typically, the first case is easier to implement, and should be
preferred, it doesn't apply to all user defined types; you can't
really use it for something like BigDecimal, for example.
 

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,756
Messages
2,569,540
Members
45,024
Latest member
ARDU_PROgrammER

Latest Threads

Top