www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Range checked assignment

reply Cecil Ward <cecil cecilward.com> writes:
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.
Sep 08 2020
next sibling parent Mitacha <mateusz.mitaszka gmail.com> writes:
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.
I believe you could use Checked (https://dlang.org/library/std/experimental/checkedint.html) with custom hook or roll your own type with appropriate operator overloading(https://dlang.org/spec/operatoroverloading.html). Code for this won't be that bad, thanks to string mixins. Just mixin("lhs" ~ op ~ "rhs") and Bob's your uncle :).
Sep 08 2020
prev sibling next sibling parent Paul Backus <snarwin gmail.com> writes:
On Tuesday, 8 September 2020 at 14:18:14 UTC, Cecil Ward wrote:
 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.
You can see a full list of overloadable operators here: https://dlang.org/spec/operatoroverloading.html Most likely you will want to handle all of the binary operators, unary operators, and assignment operators. For runtime checking, you will probably want to use an invariant, rather than writing individual checks in each member function: https://dlang.org/spec/contracts.html#Invariants
Sep 08 2020
prev sibling parent reply Harry Gillanders <contact harrygillanders.com> writes:
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) {} } }
Sep 08 2020
parent Cecil Ward <cecil cecilward.com> writes:
On Tuesday, 8 September 2020 at 16:04:29 UTC, Harry Gillanders 
wrote:
 On Tuesday, 8 September 2020 at 14:18:14 UTC, Cecil Ward wrote:
 [...]
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: [...]
Harry, thank you indeed for your generous help. Much appreciated.
Sep 09 2020