Re: Exception handling?
On Jun 24, 5:14 pm, Hector Santos <sant9...@nospam.gmail.com> wrote:
Goran wrote:
Now, supposing that you have several places where you might get into a
particular situation, and after seeing the error in your log, you
don't know where that place is (if you will, you know the line, but
it's important t know where did you come from to it). Then, you have
two possibilities:
1. have "debug" logging, turn it on and look there to see where things
start to fall apart. Combine with error log and there you are.
2. "enhance" original error while unwinding the stack (add "context"
if you will).
Or use trace tags. :)
What's that? Honest question, I don't know, and google-fu fails me.
I think maybe we are agreeing on the same issues. Trust me, this is
not an issue for me. Flows are not broken, exceptions are truly the
"exception" and not the rule and I have implemented different ideas
different ways, some simple, some more detailed for consumer usage.
So while I may say/write one thing, its not the be taken as its the
only way. :)
The only point I was making is that using a catch all, to me, is
generally more useful until you can chime in on specifics. Note I am
not speaking of an external the code block catch alls, but at the same
level. The external catch all only help you under debug mode since it
has source line numbering.
Not quite sure if I follow, and if I did, I would not be among the
90%.
I really have to press you here: so what code do you have after a
failed call?
Its covered. its not ignored goran. :)
Yeah, I didn't want to say that. Instead, I wanted to press you to
look into the following in your own code: what does your code do after
the "if (func(params)"? Chances are, nothing meaningful. So had that
function been void+exception, the "if" would have disappeared
altogether.
> Provide some examples, and I am pretty confident that,
even if you used exceptions, you could get what you need pretty easily
(that is, even if you "handle" errors, it's trivial to do what you do
either way, exceptions or error-return).
Sure, I have both. A good example was a NNTP date/time parser. The
syntax is:
yymmdd hhmmss [GMT] RFC 977
[yy]yymmdd hhmmss [GMT] RFC 3977 (replaces RFC 977)
I ended up with two parsing overloading functions
public bool GetNNTPDateTime(string sdt, out DateTime dt)
public DateTime GetNNTPDateTime(string sdt);
so I can use the first overload:
public bool onNEWGROUPS(ClientData ctcp, string args)
{
DateTime dtSince;
if (!GetNNTPDateTime(args, dtSince)) {
ctcp.SendLog("501 Invalid date/time\r\n");
return true; // RETURN ONLY FALSE TO BREAK CONN=
ECTION
}
....
return true;
}
or I can use the exception overload version:
public bool onNEWGROUPS(ClientData ctcp, string args)
{
DateTime dtSince;
try
{
dtSince = GetNNTPDateTime(args)
}
catch (Exception ex)
{
ctcp.SendLog("501 {0}\r\n",ex.Message);
return true; // RETURN ONLY FALSE TO BREAK CONN=
ECTION
}
....
return true;
}
OK, that's good enough as context. So you have "bool
GetNNTPDateTime" (lowest call level), then "bool onNEWGROUPS", and
then, (I'll presume, for sake of the example) the final "processing"
function that has to respond to something out of the scope of
discussion, and that can't throw. So you have (rough sketch):
void Process(stuff)
{
...
if (!onNewsgroup(...))
{
BreakConnection(); // I got this from RETURN ONLY FALSE TO BREAK
CONNECTION
return; // (nothing else to do)
}
...
}
Now... Imagine that these are were void functions, and that your "need
to break the connection" was handled e.g. like this:
class NonBreakingProcessingError : Exception
{ // Can use this to tell client what went wrong.
}
Now... GetNNTPDateTime uses that to signal "input" errors.
void Process(stuff)
{
try
{
....
onNewsgroup(whatever);
....
}
catch(Exception e)
{
// Nothrow zone here, hence yet another try-catch :-(
try
{
string err;
if (GetNonBreakingErrorInfo(e, err))
ctcp.SendLog(err);
else
BreakConnection();
}
catch(Exception e)
{ WeAreReallyDoomed(); /*OOM? Run GC?*/}
}
}
bool GetNonBreakingErrorInfo(Exception e, out string err)
{
if (e is NonBreakingProcessingError)
{
err = e.Message;
return true;
}
return false;
}
(Or a variant thereof, e.g. you might decide to use INonBreakingError
interface, that allows you to mix-in and simplify
GetNonBreakingErrorInfo...)
In other words, you can do what you need to do with exceptions, but
you need to cover your need to break the connection in some "bad"
cases. You don't need to do ctcp.SendLog right at the place of the
error - __there__ is the error of your ways ;-)
(I know, I know, you said that you like it that way, and I don't want
you to change your ways, but it's really this: with exceptions, things
change, and that way of yours is much less good with them...)
BTW: just as I said, onNewsgroup doesn't really do much after failed
GetNNTPDateTime. So you can just as easily do nothing about it's
failure in onNewsgroup, but still treat it further up. In your case,
the trick is to collect your "non-connection-breaking" errors
correctly inside that final catch.
I know, in "real world", things will get more complicated, but
overall, I am adamant that code structure does need to change. e.g.
GetNonfatalErrorInfo may grow, because non-connection-breaking errors
will be numerous. But you do have a lot of wiggle room there (e.g.
interface idea etc).
I also know that in this simple example, the benefit of using
exceptions isn't there (a couple of "if"s are simpler, aren't they?)
but the thing is, exactly in the real world, these "if"s accumulate
really quickly, much faster than try/catch statements. Just imagine
that you apply the above principles of mine to 4-5 additional "if"
calls.
Goran.