SafeInt

Wrapper for any basic integral type N that uses the checked operations from safeOp and rejects attempts to directly assign values that cannot be proven to be within the range representable by N. (checkedint.to() can be used to safely assign values of incompatible types, with runtime bounds checking.)

SafeInt is designed to be as interchangeable with N as possible, without compromising safety. The DebugInt template allows a variable to use SafeInt in debug mode to find bugs, and N directly in release mode for greater speed and a smaller binary.

Outside of generic code that needs to work with both SafeInt!N and N, it is generally preferable to use SmartInt instead. It generates far fewer error messages: mostly it "just works".

  • policy controls the error signalling policy (see checkedint.flags).
  • bitOps may be set to No.bitOps if desired, to turn bitwise operations on this type into a compile-time error.
  1. struct SafeInt(N, IntFlagPolicy _policy, Flag!"bitOps" bitOps = Yes.bitOps)
  2. template SafeInt(N, IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps)
    template SafeInt (
    N
    Flag!"bitOps" bitOps = Yes.bitOps
    ) if (
    (
    isIntegral!N &&
    !isUnqual!N
    )
    ||
    isCheckedInt!N
    ) {}

Examples

1 // Mixing standard signed and unsigned types is dangerous...
2 int ba = -1;
3 uint bb = 0;
4 assert(ba > bb);
5 
6 auto bc = ba + bb;
7 assert(is(typeof(bc) == uint));
8 assert(bc == 4294967295u);
9 
10 // ...that's why SafeInt doesn't allow it.
11 import checkedint.throws : SafeInt, to; // use IntFlagPolicy.throws
12 
13 SafeInt!int sa = -1;
14 SafeInt!uint sb = 0u;
15 static assert(!__traits(compiles, sa < sb));
16 static assert(!__traits(compiles, sa + sb));
17 
18 // Instead, use checkedint.to() to safely convert to a common type...
19 auto sbi = to!(SafeInt!int)(sb);
20 assert(sa < sbi);
21 auto sc = sa + sbi;
22 assert(sc == -1);
23 // (...or just switch to SmartInt.)
1 // When IntFlagPolicy.throws is set, SafeInt operations that fail at runtime will throw a CheckedIntException.
2 import checkedint.throws : SafeInt;
3 
4 SafeInt!uint sa = 1u;
5 SafeInt!uint sb = 0u;
6 
7 bool overflow = false;
8 try
9 {
10     SafeInt!uint sc = sb - sa;
11     assert(false);
12 }
13 catch (CheckedIntException e)
14 {
15     assert(e.intFlags == IntFlag.negOver);
16     overflow = true;
17 }
18 assert(overflow);
19 
20 bool div0 = false;
21 try
22 {
23     // With standard integers, this would crash the program with an unrecoverable FPE...
24     SafeInt!uint sc = sa / sb;
25     assert(false);
26 }
27 catch (CheckedIntException e)
28 {
29     // ...but with SafeInt, it just throws a normal Exception.
30     assert(e.intFlags == IntFlag.div0);
31     div0 = true;
32 }
33 assert(div0);
1 // When IntFlagPolicy.sticky is set, SafeInt operations that fail at runtime set one or more bits in IntFlags.local.
2 import checkedint.sticky : SafeInt;
3 
4 SafeInt!uint sa = 1u;
5 SafeInt!uint sb = 0u;
6 SafeInt!uint sc;
7 
8 sc = sb - sa;
9 assert(IntFlags.local == IntFlag.negOver);
10 
11 // With standard integers, this would crash the program with an unrecoverable FPE...
12 sc = sa / sb;
13 // ...but with SmartInt, it just sets a bit in IntFlags.local.
14 assert(IntFlags.local & IntFlag.div0);
15 
16 // Each flag will remain set until cleared:
17 assert(IntFlags.local.clear() == (IntFlag.negOver | IntFlag.div0));
18 assert(!IntFlags.local);

Meta