Contracts or Exceptions?
Ali Çehreli
acehreli at yahoo.com
Wed Mar 30 11:01:01 PDT 2011
On 03/30/2011 08:42 AM, Kai Meyer wrote:
> we were talking about what sort of things you can do to
> increase performance
Sorry to change the topic. :) I am fortunate that what I currently work
on does not require more than being careful about not using the wrong
algorithms and data structures.
> if (good data)
> do work
> else
> report "can't do work"
That is helpful as long as the data can be checked up front, but we
can't always be sure:
if (file_exists()) {
use_file(); // <-- the file may not exist
or
if (server_is_up()) {
talk_to_server(); // <-- the server may not be up
> Would be faster than:
>
> try
> do work
> catch
> report "can't do work"
Apparently that's what Denis' (spir) measurements show as well and
that's very unfortunate. I would like to put that slowness under the
"quality of implementation" category. Although, it's well known that
exception handling has been slow (e.g. in C++) in general too.
(Note: I heard that scope(failure) is lowered to a try-catch block by
the compiler; so it should be slow too.)
If exceptions are inherently slow, that must be because they provide
more than what we compare them against. A good example is comparing old
features of C to C++. Some people claim that calling virtual functions
is slow due to jumping off the vtbl. Correct, but let's not forget that
achieving the same in C would be slow too.
Your examples do have such a feature difference: the code that uses the
try-catch block will always execute the code in the catch clause. On the
other hand, the code that checks the data before hand may not report
"can't do work", as "do work" may get complicated in the future and may
call a function that may throw directly or indirectly. And suddenly the
function doesn't work anymore and the changes that we've made are
detached from this function. Bad bug! :)
We may argue that exceptions should not exist in D (or C++) but they are
so helpful (hey, I know we all know these :)):
- exceptions make it impossible (or very hard) to continue with bad
program state
- they allow functions to return objects by freeing the return value
from always being an error code (return values are preferable to side
effects)
- they eliminate boiler plate error management lines (To complicate
matters, when a function needs to do cleanup after an error, it may call
other functions that may return more error codes. We must be careful not
to change the value of the original error code variable.)
- they result in less lines of code (bugs live in lines of code :))
I would like to show two functions written in C and C++. To my
knowledge, they are well written and don't have any resource leaks:
// a C function
int bar_C(Resource ** in_out)
{
int err = 0;
Resource * r0 = NULL;
Resource * r1 = NULL;
err = allocate_resource(&r0);
if (err) goto finally;
err = allocate_resource(&r1);
if (err) goto finally;
/* ... use r0 and r1 here ... */
if (err) goto finally;
/* transfer ownership */
*in_out = r0;
r0 = NULL;
finally:
deallocate_resource(&r1);
deallocate_resource(&r0);
return err;
}
// The equivalent C++ function
Resource bar_CPP()
{
Resource r0(/* ... */);
Resource r1(/* ... */);
/* ... use r0 and r1 here ... */
/* transfer ownership */
return r0;
}
Of course the latter takes advantage of other features of C++, but it
also shows that there is no explicit error management when the code
relies on exceptions. bar_CPP() just does what it is supposed to do.
Yes, there may be errors but bar_CPP() doesn't care. And the callers may
or may not catch the errors, but bar_CPP() doesn't care.
> I'm not sure D's exceptions are much different than C++'s.
Yeah, it must be the same as what Digital Mars C++ compiler uses.
(Except, D also has the 'finally' clause.)
In summary: I hope I will never go back to pass-the-error-code style of
coding.
Ali
More information about the Digitalmars-d-learn
mailing list