On exceptions, errors, and contract violations

Ali Çehreli via Digitalmars-d digitalmars-d at puremagic.com
Fri Oct 3 12:00:35 PDT 2014


On 10/03/2014 10:40 AM, Sean Kelly wrote:

 > an API has no idea how important its proper function is to the
 > application writer.

Agreed.

Further, an API has no idea whether its caller is a "user" or a 
"library". (I will expand below.)

 > If a programmer passes out of range arguments to a mathematical
 > function, his logic may be faulty, but *we have no idea what
 > this means to him*.

This reminds me of a discussion we had on this forum many months ago.

If I write an API function, I have no option but to call enforce() 
inside the body of the function because I must be ready for a "user". I 
don't want my checks to be removed by the -release compiler switch.

However, if my caller happens to be a library or a lower level code of a 
program, then my enforce() calls should be turned into asserts and be 
moved to the in block.

We would like to write the checks only once but tell the compiler to use 
them as in contracts or enforce checks.

Here is an implementation of the idea if we wanted to do it ourselves:

1) Repeat the checks in both places, only one of which will be enabled 
depending on version identifiers.

double squareRoot(double value)
in
{
     mixin preConditionCheck_squareRoot!();
     check(value);
}
body
{
     mixin preConditionCheck_squareRoot!();
     check(value);

     return 42.42;
}

2) To test the idea, provide version identifiers during compilation:

/* Pick a combination of the two versions 'userCalled' and 'release'
  * below. What happens is specified after the arrow:
  *
  * a) userCalled (no release)      -> throws Exception
  * b) userCalled + release         -> throws Exception
  * c) (no userCalled, no release)  -> throws AssertError
  * d) (no userCalled) release      -> does not check
  */

// version = userCalled;
// version = release;

void main()
{
     squareRoot(-1);
}

3) Here is the horrible boiler plate that I came up with without putting 
much effort into it:

mixin template preConditionCheck_squareRoot()
{
     import std.stdio;
     import std.exception;

     void noop(T...)(T)
     {}

     void assertCaller(T...)(T args)
     {
         assert(args[0], args[1]);
     }

     version (userCalled) {
         alias checker = enforce;

     } else {
         version (release) {
             alias checker = noop;

         } else {
             alias checker = assertCaller;
         }
     }

     void check(T...)(T args)
     {
         checker(value > 0, "Value must be greater than zero.");
     }
}

Ali



More information about the Digitalmars-d mailing list