Range checked assignment

Harry Gillanders contact at harrygillanders.com
Tue Sep 8 16:04:29 UTC 2020


On Tuesday, 8 September 2020 at 14:18:14 UTC, Cecil Ward wrote:
> What I would like to do is (in pseudo-code) :
>
>    declare_var my_var : int range 0..7; // i.e. 0 <= val <= 7;
>
>     my_var = 6; // ok
>     my_var = 8; // bang ! static assert fail or assert fail at 
> runtime
>
>     my_var = 6;
>     my_var += 2; // bang ! value 8 is > 7
>
> So every assignment is range-checked at either compile-time if 
> at all possible or else at runtime. This includes things like 
> += and initialisers of course, not just straight assignment.
>
> I assumed I would have to create a struct type definition and 
> handle various operators. How many will I have to handle? I 
> would of course make it a template so I can reuse this 
> otherwise horribly repetitive code.


If you want to define an integral-like type which is more-or-less 
interchangeable with the native integral types, you'll need to 
provide the following overloads and members:

	* An enum member named `min` which provides an instance of the 
lowest possible value of the type.
	* An enum member named `max` which provides an instance of the 
highest possible value of the type.

	* A constructor.

	* `opAssign`.
	* `opOpAssign` for the operators: `-`; `+`; `/`; `*`; `%`; `^^`; 
`&`; `|`; `^`; `<<`; `>>`; `>>>`.
	* `opEquals`, which should be a const member function.
	* `opCmp`, which should be a const member function.
	* `opUnary` for the operators: `-`; `+`; `~`, which should be a 
const member function.
	* `opUnary` for the operators: `--`; `++`, which should be a 
mutable member function.
	* `opBinary` for the operators: `-`; `+`; `/`; `*`; `%`; `^^`; 
`&`; `|`; `^`; `<<`; `>>`; `>>>`, which should be a const member 
function.
	* `opCast`, which should be a const member function.

Although in your specific case, implementing the bitwise 
operators may not make sense.
Ideally, all the operator overloads, and the constructor, should 
be able to take any native integral type, and any instances of 
your type, as arguments.

Here's a skeleton implementation of an integral-like type:


	import std.algorithm;
	import std.traits;

	template isConstrainedInt (Instance)
	{
		enum bool isConstrainedInt = __traits(
			isSame,
			TemplateOf!Instance,
			ConstrainedInt
		);
	}

	template ConstrainedInt (long lower, long upper)
	if (lower <= upper)
	{
		struct ConstrainedInt
		{
			enum typeof(this) min = typeof(this)(lower);
			enum typeof(this) max = typeof(this)(upper);

			this (Integer) (const Integer value)
			if (isIntegral!Integer || isConstrainedInt!Integer)
			{}

			void opAssign (Integer) (const Integer value)
			if (isIntegral!Integer || isConstrainedInt!Integer)
			{}

			void opOpAssign (string operator, Integer) (const Integer 
value)
			if (
				canFind(
					operator,
					"-", "+", "/", "*", "%", "^^",
					"&", "|", "^",
					"<<", ">>", ">>>"
				)
				&& (isIntegral!Integer || isConstrainedInt!Integer)
			)
			{}

			bool opEquals (Integer) (const Integer value) const
			if (isIntegral!Integer || isConstrainedInt!Integer)
			{}

			int opCmp (Integer) (const Integer value) const
			if (isIntegral!Integer || isConstrainedInt!Integer)
			{}

			typeof(this) opUnary (string operator) () const
			if (
				canFind(
					operator,
					"-", "+",
					"~"
				)
			)
			{}

			typeof(this) opUnary (string operator) ()
			if (canFind(operator, "--", "++"))
			{}

			typeof(this) opBinary (string operator, Integer) (const 
Integer value) const
			if (
				canFind(
					operator,
					"-", "+", "/", "*", "%", "^^",
					"&", "|", "^",
					"<<", ">>", ">>>"
				)
				&& (isIntegral!Integer || isConstrainedInt!Integer)
			)
			{}

			T opCast (T) () const
			if (isScalarType!T || isConstrainedInt!T)
			{}
		}
	}




More information about the Digitalmars-d-learn mailing list