Throwing error is potentially buggy

W

wij

Note: This thread is from comp.lang.c++.moderated, moved here
because I failed twice to reply. Please refer the original
thread for context.

True, it's not really appropriate to show the complete code. But it's
possible to show overall structure (see below).








OK. So show some detail. How do you plan to handle error return from
receive, process, and send? Do you proceed with process if e.g.
receive fails? (I guess not). Do you "send" if "process" fails? (I
guess not.) How are receive, process and send declared? What are their
failure modes? How do you propose to signal and handle failure of
receive, process and send? You speak about possible errors, but in the
loop you've shown, there's no visible error conditions, no error
logging, no recovery, nothing. You only came up with a vague
impression that there's something wrong, but shouldn't you show, in
small snippet, what is that? People here gave you same advice, but you
as far as I can see, completely disregarded it.

Just show a rough sketch of anything using error-return, and I'll make
you a similar sketch using exceptions. It will do __exact same thing__
(that is, it will be "correct"), and there's a very big chance that it
will be shorter.

But OK, I'll give it a shot. Here's your loop with some very basic
error reporting:

// Any of these return false in case of failure
// (I presume errno is set by lower level code, but that's rather
irrelevant).
bool receive(MSG&);
bool process(const MSG&, OUT&);
bool send(const OUT&);

void log_error(const char* what)
{ // some logging.
cerr << what << " failed, errno: " << errno << endl;

}

while (true)
{
MSG msg;
if (!receive(msg))
{
log_error("receive");
continue;
}
OUT out;
if (!process(msg, out))
{
log_error("process");
continue;
}
if (!send(out))
log_error("send");

}

Or, alternatively, you could try:

while (true)
{
MSG msg;
if (receive(msg))
{
OUT out;
if (process(msg, out))
{
if (!send(out))
log_error("send");
}
else
log_error("process");
}
else
log_error("receive");

}

Here's the same thing using exceptions:

// Any of these signal error by doing throw "what" (e.g. receive will
do throw "receive").
// (Again, I presume errno is set by lower level code, but that's
rather irrelevant).
void receive(MSG&);
void process(const MSG&, OUT&);
void send(const OUT&);
void error(const char* what) { throw what; }

while (true)
try
{
MSG msg;
receive(msg);
OUT out;
process(msg, out);
send(out);
}
catch(const char* what)
{
log_error(what);
}

Now... Both snippets end up doing exact same thing, but:

1. try/catch is shorter
2. more importantly, conditional logic is out of sight; first two
variants look convoluted (and in this particular case, they indeed are
convoluted for no good reason).

Your turn to show what you think is not "correct" here.

Goran.

I am just saying one thing that "throwing error is potentially buggy".
Not that it is convenient for an error string to be written to a log
file, nor that it is elegant to mean stack unwind to exit. I am not
even really talking about simplicity neither, even whether it is an
real error or not, but merely correctness of the error reporting
mechanism. Again, I am not talking about any implement of your or his
or her correct throwing strategy, but the prevailing, the frequently
taught, probably the 'standard' one.

Let's first confine errors to only errno-like classes, assume they
all inherit from std::exception.
class E_PERM;
class E_NOENT;
class E_SRCH;
class E_INTR;
class E_IO;
class E_BADF;
class E_AGAIN;
class E_NOMEM;
class E_NOSPC;
class E_INVAL;
class E_PIPE;
class E_TIMEDOUT;
class E_NAMETOOLONG;
class E_FBIG;
class E_LOOP;
.....

// Class String has all similar members as std::string
// except throwing E_... class
//
class String {
// Take one for instance:
// Throw: E_INVAL $index is invalid
// E_FBIG String length too long
// E_NOMEM Not enough memory
//
void insert(size_t index, const String& str);
};

Now we are writting a funcion process_msg. All called functions throw
E_... in exceptional condition.

// Throw: E_INVAL $in is invalid
// E_FBIG result string is too long
// E_NOMEM not enough memory
//
void process_msg(const String& in, String& out);
{
MutexLock lock(mutex); // Not likely to fail. But shouldn't
// a robust program
catch the possible
// E_INVAL, E_PERM,
E_AGAIN from leak?
String s1,s2;
if(in.size()<=0) {
throw E_INVAL;
}
db.read(s1,1000); // may throw E_INVAL, E_FBIG, E_NOMEM,...
parse(s1,s2); // may throw E_INVAL, E_FBIG, E_NOMEM,...
process(s1,s2,out); // may throw E_INVAL, E_FBIG, E_NOMEM,...
}'

Then, a tiny part of the loop in the server-like program:

try { process_msg(in,out); }
catch(E_INVAL& e) {
send("Invalid argument",e ); // peer end could have a wrong message
// Probably one want to log message here, but there are more story
// about stack unwinding that can occur here. If error is
// misinterpreted, the program goes to std::terminate for the good.
// The error report is not just for logging purpose.
// This is one reason a function should be somewhat responsible
// to its declared thrown objects.
}
catch(E_FBIG& e) {
send("Result too long",e); // peer end could have a wrong message
}

This pattern has been explained several times. It should be clear
that at least all process_msg(..) documented errors should be caught
(probably convert to another one) in the function process_msg(..).
The prevailing "catch only those you know how to handle" style and
the "rethrow error" preaches won't work well in such error handling
or message passing scenario.

So the revised version (not the popular style):

void process_msg(const String& in, String& out)
try {
MutexLock lock(mutex); // Note: E_INVAL, E_PERM, E_AGAIN might be
// thrown here, but no
way for a local
// catch. So we added a
try/catch layer.
String s1,s2; // E_NOMEM might be thrown
if(in.size()<=0) {
throw E_INVAL; // this is the responsible one
}

try { db.read(s1,1000); }
catch(E_INVAL&) {
// What to throw? process_msg(..) only documented 3 errors.
// Do we need more errors to report? Yes, so add E_IO to mean
// vague error.
throw E_IO;
}
catch(E_INTR&) {
// Let this be handled correctly
}
catch(E_IO&) {
// E_IO had been decided for a vague error. So this error
// can't be directly reported.
throw E_IO;
}
catch(E_PERM&) {
// process_msg(..) intends to hide the implement detail that it
// reads data from a database or config file. So throw E_IO.
throw E_IO;
}
catch(E_FBIG&) {
// To be defensive. Not knowing how 'db.read(..)' will actually be
throw E_IO;
};
// There are other possible thrown type by definition.

s1+=in; // Lucky, E_FBIG is appropriate

try { parse(s1,s2); }
catch(E_INVAL&) {
throw E_IO;
}
catch(E_NOENT&) {
throw E_IO;
}
catch(E_FBIG&) {
throw E_IO;
};
// There are other possible thrown type by definition.

try { process(s1,s2,out); }
catch(E_INVAL&) {
throw E_IO;
}
catch(E_RANGE&) {
throw E_IO;
}
catch(E_BADMSG&) {
throw E_IO;
}
catch(E_FBIG&) {
throw E_IO;
};
// There are other possible thrown type by definition.

// Can the above annoying catch handlers be saved?
// My answer is no.
}
catch(E_INVAL&) {
throw; // *** BUG ***
//E_INVAL may be from the lock failure. There
// are occasionally similar codes causing such
// conflicts.
};

See, it is not easy for process_msg(..) to be merely responsible for
those documented throw types. To add to this burden, this style
require all functions of the program to follow this coding standard.

Note: Some approaches use specific name space or throw classes, but
they basically only reduce the chance of being misinterpreted, with
lots of unbalanced costs. Still, not the final solution.
 
G

Goran

I am just saying one thing that "throwing error is potentially buggy".
Not that it is convenient for an error string to be written to a log
file, nor that it is elegant to mean stack unwind to exit. I am not
even really talking about simplicity neither, even whether it is an
real error or not, but merely correctness of the error reporting
mechanism. Again, I am not talking about any implement of your or his
or her correct throwing strategy, but the prevailing, the frequently
taught, probably the 'standard' one.

Let's first confine errors to only errno-like classes, assume they
all inherit from std::exception.
class E_PERM;
class E_NOENT;
class E_SRCH;
class E_INTR;
class E_IO;
class E_BADF;
class E_AGAIN;
class E_NOMEM;
class E_NOSPC;
class E_INVAL;
class E_PIPE;
class E_TIMEDOUT;
class E_NAMETOOLONG;
class E_FBIG;
class E_LOOP;

Well, now... Why would you do something like this!? Honestly, you just
replaced a single number (errno) by an exception class! That really
does not sound reasonable. When I want something similar, I do this:

class crt_exception : ...
{
public:
crt_exception() _errno(errno) {}
};

Note that if you do this, then that large swaths of code you put down
here change drastically (they get shorter) and you do not lose
anything in any sort of precision. In other words, what you propose is
too fine-grained and I think you'll get not enough benefit for doing
it.

(As a side note... exceptions are a significantly better mechanism to
report errors then error-return in real-world code that uses errno,
because if used like above, the copy of the original errno is in
exception object, so any cleanup operation during stack unwinding,
that might e.g. reset errno, does no affect error reporting. That's
simply not the case with error-return. If it's important to preserve
"original" errno (which I believe is), then things get quite messy.)
// Class String has all similar members as std::string
// except throwing E_... class
//
class String {
 // Take one for instance:
 // Throw: E_INVAL   $index is invalid
 //              E_FBIG     String length too long
 //              E_NOMEM  Not enough memory
 //
 void insert(size_t index, const String& str);

};

Now we are writting a funcion process_msg. All called functions throw
E_... in exceptional condition.

// Throw: E_INVAL  $in is invalid
//        E_FBIG   result string is too long
//        E_NOMEM  not enough memory
//
void process_msg(const String& in, String& out);
{
 MutexLock lock(mutex);  // Not likely to fail. But shouldn't
                                              // a robust program
catch the possible
                                              // E_INVAL, E_PERM,
E_AGAIN from leak?

I reckon you mean "from lock", not "from leak"? I believe that a
robust program should __not__ catch such error. Basic question is why
would it? Look at it this way: if locking a mutex returns an error
code, what would you do if "lock" fails? I say, you can't continue.
You can only clean up whatever there is to clean and leave the
function. And that, that is the same as throwing an exception! (Bar
the "if" statement in "if (lock(mutex)) {whatever}") So throwing (and
not catching) an exception will result in shorter code.
 String s1,s2;
 if(in.size()<=0) {
   throw E_INVAL;

Honestly, this "if" here is just completely wrong. First, at this
point in code, you don't care about the size of the string, so why is
there a check? Second, why is the code written so that size can be
negative?
 }
 db.read(s1,1000);     // may throw E_INVAL, E_FBIG, E_NOMEM,...
 parse(s1,s2);            // may throw E_INVAL, E_FBIG, E_NOMEM,...
 process(s1,s2,out); // may throw E_INVAL, E_FBIG, E_NOMEM,...

}'

Yes, that's OK. All calls may throw who knows what exception, and in
the real world, there will be a great number of possible failure modes
in them.
Then, a tiny part of the loop in the server-like program:

try { process_msg(in,out); }
catch(E_INVAL& e) {
  send("Invalid argument",e ); // peer end could have a wrong message
  // Probably one want to log message here, but there are more story
  // about stack unwinding that can occur here. If error is
  // misinterpreted, the program goes to std::terminate for the good.
  // The error report is not just for logging purpose.
  // This is one reason a function should be somewhat responsible
  // to its declared thrown objects.}

Ok, here, the problem is that process_msg throws E_INVAL due to a poor
"in". There are two ways to handle this, and it's not what you have
here.

First, presuming that "in" comes from an external system that can't be
trusted, I would argue that process_msg should simply fill in "out"
with content saying that input was invalid. No exception needed.

Second, even if process_msg threw, some response is probably better
than no response, so perhaps overall loop structure should be like
this:

loop
{
try
{
receive(in);
process(in, out);
send(out);
}
catch(const exception& e)
{
try { send(ERROR_OUT(e)); } // e.g. ERROR_OUT derives from OUT
catch(const exception& e) { /*we're screwed. log error?*/ }
}
}
catch(E_FBIG& e) {
  send("Result too long",e);  // peer end could have a wrong message

}

This pattern has been explained several times. It should be clear
that at least all process_msg(..) documented errors should be caught
(probably convert to another one) in the function process_msg(..).

They should not. Why do you think that?

Look at things this way: what are you going to do if e.g. your lock
throws? I'd say, there's nothing meaningful you do. You will typically
give up with the rest of processing, you might want to signal the
error to the operator (e.g. log), and you might want to send and
"error occurred" response out.

I agree that, when something goes wrong, it's beneficial you can
convert the error in another one, but so far, your examples do not
warrant that. On top of that, it's a horrible idea to lose original
error. E.g. one of worst sins Java people do is to catch one error,
then throw another without the original error as "inner". (And due to
checked exceptions of Java, they are forced to employ said "catch -
throw another exception type" more than needed, so much so that some
well-established Java code-bases reach for (gasp!) unchecked
RuntimeException as a base for all their exception types).
The prevailing "catch only those you know how to handle" style and
the "rethrow error" preaches won't work well in such error handling
or message passing scenario.

I don't know where you got this, but it's simply not true in reality,
and it's not true in the code snippets we've seen so far. Again, I
have to press you do show what is it, in externally-observable
behavior, that you want to achieve, where use of exceptions will be in
the way. Particularly, consider my loop from above. It will pretty
much do what you want, if you just fill a couple of blanks in.
So the revised version (not the popular style):

void process_msg(const String& in, String& out)
try {
 MutexLock lock(mutex); // Note: E_INVAL, E_PERM, E_AGAIN might be
                                             // thrown here, but no
way for a local
                                             // catch. So we added a
try/catch layer.
 String s1,s2;      // E_NOMEM might be thrown
 if(in.size()<=0) {
   throw E_INVAL;   // this is the responsible one
 }

 try { db.read(s1,1000); }
 catch(E_INVAL&) {
   // What to throw? process_msg(..) only documented 3 errors.
   // Do we need more errors to report? Yes, so add E_IO to mean
   // vague error.
   throw E_IO;
 }
 catch(E_INTR&) {
   // Let this be handled correctly
 }
 catch(E_IO&) {
   // E_IO had been decided for a vague error. So this error
   // can't be directly reported.
   throw E_IO;
 }
 catch(E_PERM&) {
   // process_msg(..) intends to hide the implement detail that it
   // reads data from a database or config file. So throw E_IO.
   throw E_IO;
 }
 catch(E_FBIG&) {
   // To be defensive. Not knowing how 'db.read(..)' will actually be
   throw E_IO;
 };
 // There are other possible thrown type by definition.

 s1+=in; // Lucky, E_FBIG is appropriate

 try { parse(s1,s2); }
 catch(E_INVAL&) {
   throw E_IO;
 }
 catch(E_NOENT&) {
   throw E_IO;
 }
 catch(E_FBIG&) {
   throw E_IO;
 };
 // There are other possible thrown type by definition.

 try { process(s1,s2,out); }
 catch(E_INVAL&) {
   throw E_IO;
 }
 catch(E_RANGE&) {
   throw E_IO;
 }
 catch(E_BADMSG&) {
   throw E_IO;
 }
 catch(E_FBIG&) {
   throw E_IO;
 };
 // There are other possible thrown type by definition.

 // Can the above annoying catch handlers be saved?
 // My answer is no.}

catch(E_INVAL&) {
 throw;    // *** BUG ***
                 //E_INVAL may be from the lock failure.. There
                // are occasionally similar codes causing such
                // conflicts.

};

This snippet is completely, utterly wrong.

First, as I pointed out above, using one exception class per errno is
silly.

Second, you just lost original error, a __major__ sin in any error
reporting. E.g. you go from from E_BADMSG to E_IO). So what are you
going to send out (or log) eventually? You will see E_IO somewhere up
the stack, so only thing you can say will be "there was an IO error",
but that is obviously a blatant lie!

Third, you are catching/rethrowing way too much without achieving
anything with regard to externally-observable behavior.

Here's how your loop and your functions should work:

String class: only thing that should go wrong with it is that it runs
out of memory. Anything else should be considered a coding error,
hence responsibility of the programmer, __not__ the code. Look at your
typical string class (e.g. std::string) if you don't believe me.

receive function: should be MSG receive() or better yet, you could
have a derivation from base MSG class that knows how to turn client
your input into MSG. E.g.

class INPUT_MSG : public MSG
{
INPUT_MSG(input_stream& or_whatever);
}

INPUT_MSG should throw appropriate exception type in case of malformed
input. If input indeed is some stream, it might contain e.g. an index,
in the input stream, of the bad character. It might contain an
explanation of what is wrong with the input, e.g. "unexpected
character", or what have you. You might want to have multiple
exception types for that, simply because data explaining the error
might be diverse. That will depend on the complexity of the input,
really.

process_msg: should be OUT precess(const MSG&). It should hardly try
to catch any exceptions, because chances are, whatever happens, it
will not be able to do anything about them.

send: should be void send(const OUT&). Nothing much to say, really.

ERROR_OUT: "error" variant of the OUT: it should turn exception into
OUT. Get wild there ;-).

In the end... I think that you have massive misconceptions regarding
exceptions. I don't know why is that, perhaps because you're still in
"C" language mode, perhaps because you are in "Java" mode.

Your blanket statement "throwing is potentially buggy" is... Well, a
blanket statement. It's true if there's a bug in code, it's false if
there isn't. I suggest that you either

1. continue working without exceptions (but trust me, that will not
pan out well if you're doing C++)

2. when you look at any particular situation and don't know what to
do, ask a specific question here (or no moderated).

3. on a more general note, try to understand more about overall use of
exceptions. I feel that you're doing it all wrong because, like many
other people, you can't wrap your head around premature exit due to
exceptions. Specifically, how is error info propagated when that
happens.

Goran.

P.S. If I sound like an arrogant prick, I apologize ;-)
 
W

wij

Well, now... Why would you do something like this!? Honestly, you just
replaced a single number (errno) by an exception class! That really
does not sound reasonable. When I want something similar, I do this:

class crt_exception : ...
{
public:
crt_exception() _errno(errno) {}

};

Note that if you do this, then that large swaths of code you put down
here change drastically (they get shorter) and you do not lose
anything in any sort of precision. In other words, what you propose is
too fine-grained and I think you'll get not enough benefit for doing
it.

(As a side note... exceptions are a significantly better mechanism to
report errors then error-return in real-world code that uses errno,
because if used like above, the copy of the original errno is in
exception object, so any cleanup operation during stack unwinding,
that might e.g. reset errno, does no affect error reporting. That's
simply not the case with error-return. If it's important to preserve
"original" errno (which I believe is), then things get quite messy.)





I reckon you mean "from lock", not "from leak"? I believe that a
robust program should __not__ catch such error. Basic question is why
would it? Look at it this way: if locking a mutex returns an error
code, what would you do if "lock" fails? I say, you can't continue.
You can only clean up whatever there is to clean and leave the
function. And that, that is the same as throwing an exception! (Bar
the "if" statement in "if (lock(mutex)) {whatever}") So throwing (and
not catching) an exception will result in shorter code.


Honestly, this "if" here is just completely wrong. First, at this
point in code, you don't care about the size of the string, so why is
there a check? Second, why is the code written so that size can be
negative?



Yes, that's OK. All calls may throw who knows what exception, and in
the real world, there will be a great number of possible failure modes
in them.



Ok, here, the problem is that process_msg throws E_INVAL due to a poor
"in". There are two ways to handle this, and it's not what you have
here.

First, presuming that "in" comes from an external system that can't be
trusted, I would argue that process_msg should simply fill in "out"
with content saying that input was invalid. No exception needed.

Second, even if process_msg threw, some response is probably better
than no response, so perhaps overall loop structure should be like
this:

loop
{
try
{
receive(in);
process(in, out);
send(out);
}
catch(const exception& e)
{
try { send(ERROR_OUT(e)); } // e.g. ERROR_OUT derives from OUT
catch(const exception& e) { /*we're screwed. log error?*/ }
}

}


They should not. Why do you think that?

Look at things this way: what are you going to do if e.g. your lock
throws? I'd say, there's nothing meaningful you do. You will typically
give up with the rest of processing, you might want to signal the
error to the operator (e.g. log), and you might want to send and
"error occurred" response out.

I agree that, when something goes wrong, it's beneficial you can
convert the error in another one, but so far, your examples do not
warrant that. On top of that, it's a horrible idea to lose original
error. E.g. one of worst sins Java people do is to catch one error,
then throw another without the original error as "inner". (And due to
checked exceptions of Java, they are forced to employ said "catch -
throw another exception type" more than needed, so much so that some
well-established Java code-bases reach for (gasp!) unchecked
RuntimeException as a base for all their exception types).


I don't know where you got this, but it's simply not true in reality,
and it's not true in the code snippets we've seen so far. Again, I
have to press you do show what is it, in externally-observable
behavior, that you want to achieve, where use of exceptions will be in
the way. Particularly, consider my loop from above. It will pretty
much do what you want, if you just fill a couple of blanks in.














This snippet is completely, utterly wrong.

First, as I pointed out above, using one exception class per errno is
silly.

Second, you just lost original error, a __major__ sin in any error
reporting. E.g. you go from from E_BADMSG to E_IO). So what are you
going to send out (or log) eventually? You will see E_IO somewhere up
the stack, so only thing you can say will be "there was an IO error",
but that is obviously a blatant lie!

Third, you are catching/rethrowing way too much without achieving...

¾\Ū§ó¦h >>

What should a function(e.g. process()) respond if it caught
std::invaid_argument (many are from elementary functions)?

Let's say you don't (or do) use Standard Library. Then, what should
a user function respond to your crt_exception (having gone through
many layers of fucntions) in a server loop (should do
everything possible to keep on servicing) in your design?
 
W

wij

What should a function(e.g. process()) respond if it caught
std::invaid_argument (many are from elementary functions)?

Let's say you don't (or do) use Standard Library. Then, what should
a user function respond to your crt_exception (having gone through
many layers of fucntions) in a server loop (should do
everything possible to keep on servicing) in your design?
 
Ö

Öö Tiib

What should a function(e.g. process()) respond if it caught
std::invaid_argument (many are from elementary functions)?

Let's say you don't (or do) use Standard Library. Then, what should
a user function respond to your crt_exception (having gone through
many layers of fucntions) in a server loop (should do
everything possible to keep on servicing) in your design?

Your code tried to do something. It failed completely. It did catch
exception with information about reasons of failure.

What should it do now? Only an author of your software can tell what
it should do when it fails a particular operation for particular
reasons. Making another ton of exception classes is certainly not
helping any.
 
G

Goran

What should a function(e.g. process()) respond if it caught
std::invaid_argument (many are from elementary functions)?

Let's say you don't (or do) use Standard Library. Then, what should
a user function respond to your crt_exception (having gone through
many layers of fucntions) in a server loop (should do
everything possible to keep on servicing) in your design?

Note that invalid_agrument is a logic_error. Anything that derives
from logic_error should lead to termination as quickly as possible,
because logic_error signal errors in coding^^^. That is, process()
should do absolutely nothing about it, nor should any other function
up until the very top of the stack. At the end, code should just die
and __consequently be fixed__. That's how invalid_argument should be
used.

Similarly, e.g. range_error is a programming error, too. In fact, most
of std:: exceptions, bar bad_alloc should be treated as a programming
error, which, in my book, cause code to die as soon as possible and
hence force development to fix it.

Consequence of that is that "user" code does never throws any of these
exceptions with intention to signal execution errors, either.

^^^ exceptions are not supposed to be used to "handle" coding errors
(bugs). This is a common misconception. They can be used to signal
them, but only for the purpose of a clean exit out of the program. A
bug requires a fix, not "error handling".

Goran.
 
W

wij

Your code tried to do something. It failed completely. It did catch
exception with information about reasons of failure.

What should it do now? Only an author of your software can tell what
it should do when it fails a particular operation for particular
reasons. Making another ton of exception classes is certainly not
helping any.

If it failed completely, it is because that program throws error.
Otherwise, show me ONE example of throwing standard that most program
can adopt, or else.
 
W

wij

......
^^^ exceptions are not supposed to be used to "handle" coding errors
(bugs). This is a common misconception. They can be used to signal
them, but only for the purpose of a clean exit out of the program. A
bug requires a fix, not "error handling".

Goran.

Agree.
 
G

Goran

Let's say you don't (or do) use Standard Library. Then, what should
a user function respond to your crt_exception (having gone through
many layers of fucntions) in a server loop (should do
everything possible to keep on servicing) in your design?

Sorry, I went over crt_exception. I agree with Öö Tiib - it's up to
the rest of the code, so it's more of a question for you.

I'd say that it should try to send out an appropriate "error" response
(e.g. through send(ERROR_OUT(e)) in my loop from a previous post. When
in catch() of my loop hereabove, perhaps the first thing to do is to
log an error, so that operator can see that there was something wrong.

In a way, I feel that you are asking me "what should my program do if
X happens", where X is rather vague (some CRT function failed). Not
knowing about requirements and said function, I can only speculate on
what might be a good idea. One thing I am sure, though: it's HIGHLY
improbable that using exceptions will prevent you from achieving
desired behavior.

Say that a failure was caused by denied access to a file (e.g. fopen
fails and errno is EACCESS). In that case, I'd say that you really
should derive from crt_exception so that you can note the name of the
file. But you have to decide, on a case-by case basis, what
information about the error you need to pass on. Luckily, that is easy
with exceptions (and not easy with error-return).

Goran.
 
Ö

Öö Tiib

If it failed completely, it is because that program throws error.

No. Your procedure doing the operation discovered that it can not
proceed that operation. Something that should be there did not exist,
or was unaccessible or accessing it did time out ... and so on. So it
threw error because there was error. Like Goran said on lot of the
cases it fails because it was programmed with bugs. It did not fail
because it threw an error.
Otherwise, show me ONE example of throwing standard that most program
can adopt, or else.

I think that Herb Sutter discussed the issues with exception handling
quite well in his books.

Exception handling coding policy is something that each team that uses
exceptions should adopt themselves. It is all about what from where
and when the team should throw, what from where and when they may not
throw (or let the exceptions to fly past) and how they should catch
and handle them.

Example of basic standard: "If your program discovers a bug in it, it
should tell honestly to its user that it contains a bug and die
horribly with sorry face." That is what i think most programs should
adopt.
 
W

wij

No. Your procedure doing the operation discovered that it can not
proceed that operation. Something that should be there did not exist,
or was unaccessible or accessing it did time out ... and so on. So it
threw error because there was error. Like Goran said on lot of the
cases it fails because it was programmed with bugs. It did not fail
because it threw an error.

It was deliberately made to demonstrate certain kind of problem if
functions tried to report error by throw.
I think that Herb Sutter discussed the issues with exception handling
quite well in his books.

This is non-topic: I have one or two of his book, but too wordy for
me.
Exception handling coding policy is something that each team that uses
exceptions should adopt themselves. It is all about what from where
and when the team should throw, what from where and when they may not
throw (or let the exceptions to fly past) and how they should catch
and handle them.

Example of basic standard: "If your program discovers a bug in it, it
should tell honestly to its user that it contains a bug and die
horribly with sorry face." That is what i think most programs should
adopt.

Sounds reasonable to me.
 
W

wij

Sorry, I went over crt_exception. I agree with Öö Tiib - it's up to
the rest of the code, so it's more of a question for you.

I'd say that it should try to send out an appropriate "error" response
(e.g. through send(ERROR_OUT(e)) in my loop from a previous post. When
in catch() of my loop hereabove, perhaps the first thing to do is to
log an error, so that operator can see that there was something wrong.

Functions need to decide what to continue, action A or action B...,
logging error or anything involving debugging are irrelevant.
In a way, I feel that you are asking me "what should my program do if
X happens", where X is rather vague (some CRT function failed). Not
knowing about requirements and said function, I can only speculate on
what might be a good idea. One thing I am sure, though: it's HIGHLY
improbable that using exceptions will prevent you from achieving
desired behavior.

Say that a failure was caused by denied access to a file (e.g. fopen
fails and errno is EACCESS). In that case, I'd say that you really
should derive from crt_exception so that you can note the name of the
file. But you have to decide, on a case-by case basis, what
information about the error you need to pass on. Luckily, that is easy
with exceptions (and not easy with error-return).

Goran.

I don't know what CRT means.

And am a little bit confused. Because you said " ^^^ exceptions are
not
supposed to be used to "handle" coding errors". On the other hand you
said "One thing I am sure, though: it's HIGHLY improbable that using
exceptions will prevent you from achieving desired behavior."
And said something about crt_exception, so I'd like to have a look.
 
G

Goran

Functions need to decide what to continue,  action A or action B...,
logging error or anything involving debugging are irrelevant.




I don't know what CRT means.

CRT = C (language) R(untime) L(ibrary), that is, "standard" C
functions. I used crt_exception with idea that execution errors come
from CRT functions, where function has a return value that indicates
an error and error number is in errno variable (typical for CRT
functions).
And am a little bit confused. Because you said " ^^^ exceptions are
not
supposed to be used to "handle" coding errors".

What I meant was that it's wrong to use exceptions as a means to solve
errors that stem from coding errors. E.g.

std::vector<int> v;
v.push_back(1);
int i = v[2]; // Coding error here. Exceptions should NOT be used to
"handle" this error.
// Instead, programmer should fix the coding error
// (v has only one element, but code asked for the seccond).
On the other hand you
said "One thing I am sure, though: it's HIGHLY improbable that using
exceptions will prevent you from achieving desired behavior."

I wanted to say that, starting from a specification (what you want
your program to do) you __can__ do it regardless of whether you use
error-return or if you use exceptions. I said that because I am under
impression that you think that use of exceptions somehow prevent you
from achieving desired program behavior.

Goran.
 
A

Alf P. Steinbach

* (e-mail address removed), on 10.06.2010 16:54:
What should a function(e.g. process()) respond if it caught
std::invaid_argument (many are from elementary functions)?

The bug should be fixed. Presumably the running program cannot do that.


Cheers & hth.,

- Alf
 
W

wij

......
I wanted to say that, starting from a specification (what you want
your program to do) you __can__ do it regardless of whether you use
error-return or if you use exceptions. I said that because I am under
impression that you think that use of exceptions somehow prevent you
from achieving desired program behavior.

Goran.

How would you implement this function correctly as above, using
functions that all report errors by throwing class E_....?
Probably fine, if all using demonstrate-able crt_exception.

// Throw: E_INVAL $in is invalid
// E_FBIG result string is too long
// E_NOMEM not enough memory
// E_IO vague error
//
void process_msg(const String& in, String& out);

I mean correct by this:

try { process_msg(in,out); }
catch(E_INVAL&) {
// $in is invalid
}
catch(E_FBIG&) {
// Result string would exceed , say 1000 characters
};
 
W

wij

* (e-mail address removed), on 10.06.2010 16:54:




The bug should be fixed. Presumably the running program cannot do that.

Cheers & hth.,

- Alf

std::invalid_argument is not necessarily indicating a program bug,
though most likely. E.g. it may be from a parser, or from vector<T>
(T may involve more than two types) or may be the source is
purposeful. The point is that when caught, like the process_msg(..)
example above, application can't rely on it to determine what to
do next but rethrow.
I should have picked another one for the instance, though.
 
A

Alf P. Steinbach

* (e-mail address removed), on 11.06.2010 19:36:
std::invalid_argument is not necessarily indicating a program bug,
though most likely. E.g. it may be from a parser, or from vector<T>
(T may involve more than two types) or may be the source is
purposeful. The point is that when caught, like the process_msg(..)
example above, application can't rely on it to determine what to
do next but rethrow.
I should have picked another one for the instance, though.

If the exception is of type std::invalid_argument but *means* something
different, then treat it according to its meaning. Anyway, the question is then
meaningless. The question, how to handle std::invalid_argument, only makes sense
when std::invalid_argument means std::invalid_argument, and only for a general
context (e.g., not exploring which values do not throw that exception).


Cheers & hth.,

- Alf
 
G

Goran

How would you implement this function correctly as above, using
functions that all report errors by throwing class E_....?

(please see below...)
Probably fine, if all using demonstrate-able crt_exception.

// Throw: E_INVAL $in is invalid
// E_FBIG result string is too long
// E_NOMEM not enough memory
// E_IO vague error
//
void process_msg(const String& in, String& out);

I mean correct by this:

try { process_msg(in,out); }
catch(E_INVAL&) {
// $in is invalid}

catch(E_FBIG&) {
// Result string would exceed , say 1000 characters};

I think that these catches should not be here. To me, this code is
incorrect in itself. It's therefore useless discussing how process_msg
should look like so that your try/catch is somehow "correct".

Take E_INVAL. (BTW, that should be catch "const (E_INVAL&)", note the
use of const). I'll presume that that E_INVAL means that "in" was bad
(although that's a really bad presumption, because A LOT of other
things can fail with E_INVAL). That means that out is bad too,
because, if you didn't understand "in", how can you produce "out"? And
if you didn't produce "out", how can you call "send"? (And indeed,
becasue you put "process" in try/catch, "send" will be called, won't
it?

I'll repeat my loop from above, that's how your code can look like and
be correct:

loop
{
try
{
IN in;
receive(in);
OUT out;
process(in, out);
send(out);
}
catch(const exception& e)
{
try { send(ERROR_OUT(e)); } // e.g. ERROR_OUT derives from OUT
catch(const exception& e) { /*we're screwed. log error?*/ }
}
}

In fact, you can (and should, really) do this:

IN receive();
OUT process(const IN&);
void send(const OUT& out);

loop
{
try
{
send(process(receive()));
}
catch(const exception& e)
{
try { send(ERROR_OUT(e)); } // e.g. ERROR_OUT derives from OUT
catch(const exception& e) { /*we're screwed. log error?*/ }
}
}

Now... Take E_INVAL again. Let's presume that E_INVAL means that
process() encountered invalid input. You can do e.g. this:

struct OUT
{
string _text; // to be sent
};
class ERROR_OUT: public OUT
{
public:
ERROR_OUT(const exception& e)
{
if (dynamic_cast<const E_INVAL>(&e)
_text = "input was bad"; // ^^^
else
_text = e.what(); ###
}
};

Now, if you use E_INVAL for other purposes, then clearly ^^^ is
incorrect. It's your responsibility to make that correct (e.g. by
using another exception type, or, better yet, just make what() of your
exception clear enough so that you can just do ### regardless of
that).

Alternatively, you can turn things on their heads and say that
"process" should produce "out" even when "in" was bad. In that case,
"process" must treat "invalid input" cases and fill "out" accordingly.
But that depends on your design, not on use of exceptions.

Goran.
 
G

Goran

std::invalid_argument is not necessarily indicating a program bug,
though most likely. E.g. it may be from a parser, or from vector<T>
(T may involve more than two types) or may be the source is
purposeful.

Then a parser, or T, have a bug (they are using invalid_argument in a
bad way). Fix parser and/or T first.

Goran.
 
W

wij

It seemed to me you are talking about inspecting the thrown object
dynamically for real errors is the correct way.

I switch to normal codes because this can serve as a hint to the
answer of the other thread. (But I am not familiar with standard
library, I use mine.)

std::vector<T> v1,v2;
......
try { v1.insert(itr,v2); }
catch(const std::invalid_argument& e) {
// Run-time check e for the real meaning, never by type.
}
catch(const std::length_error& e) {
// Run-time check e for the real meaning, never by type.
};

I do regard this as the fundamental defect of C++ language.
Am I nut?
 

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

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top