Martijn said:
You'll have a hard time tracing exceptions with this design. Why not
define a trace object, that prints in constructor and destructor? Much
easier to use and works with multiple returns and exceptions. It's what I
do if I need this.
So, each time you want to debug a function with multiple return points,
you must:
1) Define a class with member references to all of the function's local
variables; at least, the ones you want to trace.
2) Find a cozy place somewhere before any of the function's return
points, but after all the traced variables have been defined. IME, this
is often impossible. Here is a simplistic case that reflects the common
practice of defining some variable, returning if a check succeeds, and
otherwise defining some other variables:
template< typename T >
void sort_two_objects( T a, T b )
{
bool already_sorted = a < b;
// A Trace object defined here cannot trace the value of copy_of_a,
// even if the return statement is not used.
if( already_sorted )
{
return;
}
// A Trace object defined here will not be destructed if the return
// statement is used.
T copy_of_a = a;
a = b;
b = copy_of_a;
}
I never was a fan of not allowing multiple exit points, but exceptions
embedded it in the language, so I guess we better get used to it. I know I
don't have any problem with it.
Exceptions do provide "extra" exit points, but they are not the same as
return statements. Exceptions carry information back up the call stack
about what went wrong, and why. Often, the information obtained in a
high-level catch block is sufficient, and there is no need to look at
the lower-level code at all. Even when there is such a need, since my
exceptions always include the line number and file number of the
corresponding throw statement, I know exactly which exit point to watch.
Obviously, multiple exit points, like any language feature can be used
and mis-used. I concur that they can introduce problems, but only if
mis-used. My personal guideline in this is that the code should read like
a story. If not, you have a maintenance problem at least. So this is not
really related to multiple exit points, but to obfuscated coding.
I've heard that theory before. The fact is that programs are not
stories. When I'm debugging a large piece of code, I have no desire to
start at the beginning and proceed to the point of the problem. The
time required for such an approcach is linear with the number of lines
leading to the point of the problem. By doing instead a binary search
for the problem, debug time is significantly reduced. This approach is
eased greatly by the absence of spurious return statements. Any extra
return statement is what I would call "obfuscated code."
-Jeff