Compile time range checking/asserts

Jonathan M Davis jmdavisProg at gmx.com
Tue Sep 14 23:26:57 PDT 2010


On Tuesday 14 September 2010 22:42:07 Borden Rhodes wrote:
> Good morning, list,
> 
> I know that D has support for ranges in for-each statements and in array
> bounds checking, but I'm curious if it also has a facility for compile-time
> range checking or assertions on individual variables.
> 
> For example, using the Java Tutorial's venerable Bicycle class, say I
> had a property <code>int gear;</code> which indicated the bicycle's current
> gear.  Let's say all bicycles of this class have 6 gears.  Typically, I
> would write a changeGear method as:
> 
> <code>
> void changeGear(int newGear) {
>         if ( 1 <= newGear && newGear <= 6)
>                 gear = newGear;
>         else
>                 throw SomeKindOfException();
> }
> </code>
> 
> which would include the if statement to make sure that newGear would be
> passed a number in the correct range and a throw statement to deal with
> cheaters.
> 
> Does D allow - or would there be logic in creating - an ability to define
> the acceptable ranges when the variable is initialised?  So, say I wanted
> gear only to accept values between 1 and 6 inclusive.  Could I declare
> something to the effect of:
> 
> <code>int gear {1...6} = 1;</code>
> 
> Which could read, "create a new int, called gear, which only accepts values
> between 1 and 6, inclusive, and initialise it to 1."  Then, if I, or some
> coder who forgot how many gears my Bicycle has, tried to call:
> 
> </code>bicycle.changeGear( 12 );</code>
> 
> the compiler would catch that <code>gear = newGear;</code> is assigning a
> value out of range and throw a compile-time error.  Can D do this?  Should
> it do this?
> 
> With thanks,
> 
> Borden


You cannot restrict a primitive type to a specific range of values. You can, 
however, use contracts to ensure at runtime that a value is within a specific 
range. e.g.

class Bicycle
{
	void changeGear(int gear)
	in
	{
		assert(gear >= 1 && gear <= 6);
	}
	body
	{
		//do whatever changeGear() does
	}
}


So, if you compile in debug mode (i.e. without -release), then the assertion in 
the in block will run before the function body does. If it fails, an AssertError 
will be thrown. You have it check at compile time though.

Any code which is run at compile-time (e.g. when an enum is instantiated) will 
have its contracts run when at compile-time when the code is run, but that's 
only for code which is run at compile-time using CTFE (Compile-Time Function 
Evaluation).

You could use an enum like so

enum Gear : int {one = 1, two, three, four, five, six};


and the values will be fixed. But Even if you use Gear as the parameter 
changeGear(), it won't do any checking beyond the range of the enum type (int in 
this case). So, while D has fantastic code generation capabilities, it doesn't 
really have much in the way of doing compile-time checks of values outside of 
CTFE or template constraints.

So, typicall what you would do is use contracts (in for pre-conditions, out for 
post-conditions, and invariant for class/struct invariants) which would run when 
the program is run in debug mode.

- Jonathan M Davis


More information about the Digitalmars-d-learn mailing list