The Right Approach to Exceptions

H. S. Teoh hsteoh at quickfur.ath.cx
Fri Feb 24 07:57:13 PST 2012


On Thu, Feb 23, 2012 at 07:06:02PM -0500, Jonathan M Davis wrote:
> On Thursday, February 23, 2012 15:18:27 H. S. Teoh wrote:
> > On Thu, Feb 23, 2012 at 12:07:40PM -0800, Jonathan M Davis wrote:
> > In my book, a linked library shares equal status with the "main
> > program", therefore the definition of "user input" still sits at the
> > internal-to-program and external boundary.
> 
> Yes, "in your book." Some people will agree with you and some won't.
> It really depends on what the code is doing though IMHO. In some
> cases, one is better and in some cases, the other is better. But it
> _is_ important to remember that there's a big difference between
> linking against a library over which you have control and a 3rd party
> library.
> 
> And there are times when it just plain makes more sense to have a
> function which throws an exception on bad input regardless of whether
> it's an "internal" function or not. For instance, if you want to
> convert a string to something else (e.g. with SysTime's
> fromISOExtString or even just with std.conv.to), you need to actually
> verify that the string has a value which can be correctly converted.
> It's actually cheaper to have the function doing the conversion do the
> checking rather than have another function do a check first, and then
> have the converting function not check (save perhaps for an assertion
> outside of release mode), because then you'll be processing the string
> _twice_.

I guess this is a judgment call. Personally, I would consider arguments
to string conversion functions to be "user input" even though,
technically speaking, you could just be passing a byte array literal to
it, in which case it's just a case of bad input parameters.


> This is _not_ a cut-and-dried issue. Sometimes DbC makes more sense,
> and sometimes defensive programming does. You pick the one that works
> best for a given situation.
> 
> The whole thing is a gray area, and you're not going to get a
> consensus that a library should always use DbC on its functions or
> that it should always use defensive programming.

I wasn't trying to say that library code should always use DbC and
application code should always use defensive programming. I'm saying
that if it makes sense for a function to use DbC (or vice versa) then it
should use DbC regardless of whether it's in a library or not.  If I
were to write a string conversion function, for example, I wouldn't use
contracts to enforce the right encoding, regardless of whether it's in a
library or in application code. I would use exceptions, simply because
that's what makes sense in this case. Just because something is in the
library shouldn't change whether DbC or defensive programming is used.
It's the semantics that matter, not whether it's in a library.


[...]
> > No need to templatize anything, just ship two versions of the
> > library, one with DbC compiled in, one without. Let the user decide
> > which one to link in.
> 
> There _is_ a need to do that if the caller wants to control whether an
> assertion or an exception is used. There's also a need if you want to
> enable it in some places and not in others. However, the reality of
> the matter is that using a debug version of a library is as close as
> you're likely to get.  And I'm certainly not arguing that templatizing
> functions in this manner would be a good idea. I'm just pointing aut
> that there are issues with how DbC is currently implemented.
[...]

Actually, I wonder if it makes sense for the compiler to insert
in-contract code in the *caller* instead of the callee. Conceptually
speaking, an in-contract means "you have to fulfill these conditions
before calling this function". So why not put the check in the caller?

Similarly, an out-contract means "this function's return value will
satisfy these conditions" - so let the caller verify that this is true.

Semantically it amounts to the same thing, but this gives us more
flexibility: the library doesn't have to be compiled with contracts
on/off, the contracts are in the library API, and the user tells the
compiler whether or not to wrap the contract code around each call to
the library.

(Yes this bloats the code everywhere a DbC function is called, but this
is supposed to be done in non-release builds only anyway, so I don't
think that matters so much.)


T

-- 
Тише едешь, дальше будешь.


More information about the Digitalmars-d mailing list