Re: Throwing error is potentially buggy
On Jun 11, 6:03 pm, w...@seed.net.tw wrote:
......
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_....?
(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.