Good Contract programming idiom?

bearophile bearophileHUGS at lycos.com
Tue Mar 2 04:44:19 PST 2010


This is currently present in this Wikipedia page:
http://en.wikipedia.org/wiki/Class_invariant#D

It shows a normal way to use contract programming in D:

class Date {
    private int day, hour;
 
    invariant() {
        assert(1 <= day && day <= 31);
        assert(0 <= hour && hour < 24);
    }
 
    this(int d, int h) //constructor
        in {
            assert(1 <= d && d <= 31);
            assert(0 <= h && h < 24);
        }
        out {
            assert(day == d);
            assert(hour == h);
        }
        body {
            day = d;
            hour = h;
        }
 
    public void setDay(int d)
        in { assert(1 <= d && d <= 31); }
        out { assert(day == d); }
        body {
            day = d;
        }
 
    public void setHour(int h)
        in { assert(0 <= h && h < 24); }
        out { assert(hour == h); }
        body {
            hour = h;
        }
}


But generally public functions must test arguments in release mode too, so I (and lot of other people) don't like to use asserts in this situation. It's better to use an always present test followed by a throw (so such test is not in the precondition).

So this is the code that I prefer:

/// Thrown when an illegal argument is encountered.
class IllegalArgumentException : Exception {
    this(string msg) {
        super(msg);
    }
}
 
 
class Date {
    private int day, hour;
 
    invariant() {
        assert(1 <= day && day <= 31);
        assert(0 <= hour && hour < 24);
    }
 
    this(int d, int h) //constructor
        out {
            assert(day == d);
            assert(hour == h);
        }
        body {
            if (d < 1 || d > 31)
                throw new IllegalArgumentException("Wrong day.");
            if (h < 0 || h >= 24)
                throw new IllegalArgumentException("Wrong hour.");
 
            day = d;
            hour = h;
        }
 
    public void setDay(int d)
        out { assert(day == d); }
        body {
            if (d < 1 || d > 31)
                throw new IllegalArgumentException("Wrong day.");
            day = d;
        }
 
    public void setHour(int h)
        out { assert(hour == h); }
        body {
            if (h < 0 || h >= 24)
                throw new IllegalArgumentException("Wrong hour.");
            hour = h;
        }
}
 
 
void main() {
    Date d = new Date(/*d=*/10, /*h=*/5);
    d.setHour(30); // throws
}

What do you think? Do you agree that's better to use exceptions like this to test arguments in public methods (instead of using asserts in preconditions)?

I have had to define locally that IllegalArgumentException because I think it's missing still in Phobos2 core.exception.
Those /*d=*/   /*h=*/  are useful because D doesn't support named arguments yet.

Bye,
bearophile



More information about the Digitalmars-d mailing list