std.checkedint

This module defines facilities for efficient checking of integral operations against overflow, casting with loss of precision, unexpected change of sign, etc. The checking (and possibly correction) can be done at operation level, for example opChecked!"+"(x, y, overflow) adds two integrals x and y and sets overflow to true if an overflow occurred. The flag overflow (a bool passed by reference) is not touched if the operation succeeded, so the same flag can be reused for a sequence of operations and tested at the end.

Issuing individual checked operations is flexible and efficient but often tedious. The Checked facility offers encapsulated integral wrappers that do all checking internally and have configurable behavior upon erroneous results. For example, Checked!int is a type that behaves like int but aborts execution immediately whenever involved in an operation that produces the arithmetically wrong result. The accompanying convenience function checked uses type deduction to convert a value x of integral type T to Checked!T by means of checked(x). For example:

void main()
{
   import std.checkedint, std.stdio;
   writeln((checked(5) + 7).get); // 12
   writeln((checked(10) * 1000 * 1000 * 1000).get); // Overflow
}

Similarly, checked(-1) > uint(0) aborts execution (even though the built-in comparison int(-1) > uint(0) is surprisingly true due to language's conversion rules modeled after C). Thus, Checked!int is a virtually drop-in replacement for int useable in debug builds, to be replaced by int in release mode if efficiency demands it.

Checked has customizable behavior with the help of a second type parameter, Hook. Depending on what methods Hook defines, core operations on the underlying integral may be verified for overflow or completely redefined. If Hook defines no method at all and carries no state, there is no change in behavior, i.e. Checked!(int, void) is a wrapper around int that adds no customization at all.

This module provides a few predefined hooks (below) that add useful behavior to Checked:

These policies may be used alone, e.g. Checked!(uint, WithNaN) defines a uint-like type that reaches a stable NaN state for all erroneous operations. They may also be "stacked" on top of each other, owing to the property that a checked integral emulates an actual integral, which means another checked integral can be built on top of it. Some combinations of interest include:

The hook's members are looked up statically in a Design by Introspection manner and are all optional. The table below illustrates the members that a hook type may define and their influence over the behavior of the Checked type using it. In the table, hook is an alias for Hook if the type Hook does not introduce any state, or an object of type Hook otherwise.

,

Hook member Semantics in Checked!(T, Hook)
defaultValue If defined, Hook.defaultValue!T is used as the

default initializer of the payload.

min If defined, Hook.min!T is used as the minimum value of

the payload.

max If defined, Hook.max!T is used as the maximum value of

the payload.

hookOpCast If defined, hook.hookOpCast!U(get) is forwarded

to unconditionally when the payload is to be cast to type U.

onBadCast If defined and hookOpCast is not defined,

onBadCast!U(get) is forwarded to when the payload is to be cast to type U and the cast would lose information or force a change of sign.

hookOpEquals If defined, hook.hookOpEquals(get, rhs) is

forwarded to unconditionally when the payload is compared for equality against value rhs of integral, floating point, or Boolean type.

hookOpCmp If defined, hook.hookOpCmp(get, rhs) is

forwarded to unconditionally when the payload is compared for ordering against value rhs of integral, floating point, or Boolean type.

hookOpUnary If defined, hook.hookOpUnary!op(get) (where op

is the operator symbol) is forwarded to for unary operators `-` and `~`. In addition, for unary operators `++` and `--`, hook.hookOpUnary!op(payload) is called, where payload is a reference to the value wrapped by Checked so the hook can change it.

hookOpBinary If defined, hook.hookOpBinary!op(get, rhs)

(where op is the operator symbol and rhs is the right-hand side operand) is forwarded to unconditionally for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.

hookOpBinaryRight If defined, hook.hookOpBinaryRight!op(lhs, get) (where op is the operator symbol and

lhs is the left-hand side operand) is forwarded to unconditionally for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.

onOverflow If defined, hook.onOverflow!op(get) is forwarded

to for unary operators that overflow but only if hookOpUnary is not defined. Unary `~` does not overflow; unary `-` overflows only when the most negative value of a signed type is negated, and the result of the hook call is returned. When the increment or decrement operators overflow, the payload is assigned the result of hook.onOverflow!op(get). When a binary operator overflows, the result of hook.onOverflow!op(get, rhs) is returned, but only if Hook does not define hookOpBinary.

hookOpOpAssign If defined, hook.hookOpOpAssign!op(payload,

rhs) (where op is the operator symbol and rhs is the right-hand side operand) is forwarded to unconditionally for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.

onLowerBound If defined, hook.onLowerBound(value, bound)

(where value is the value being assigned) is forwarded to when the result of binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=` is smaller than the smallest value representable by T.

onUpperBound If defined, hook.onUpperBound(value, bound)

(where value is the value being assigned) is forwarded to when the result of binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=` is larger than the largest value representable by T.

hookToHash If defined, hook.hookToHash(payload)

(where payload is a reference to the value wrapped by Checked) is forwarded to when toHash is called on a Checked type. Custom hashing can be implemented in a Hook, otherwise the built-in hashing is used.

Source: std/checkedint.d

Types 7

structChecked(T, Hook = Abort) if (isIntegral!T || is(T == Checked!(U, H), U, H))

Checked integral type wraps an integral T and customizes its behavior with the help of a Hook type. The type wrapped must be one of the predefined integrals (unqualified), or another instance of Checked.

Parameters

Ttype that is wrapped in the Checked type
Hookhook type that customizes the behavior of the Checked type
Methods
auto get() inoutReturns: A copy of the underlying value.
Checked opAssign(U)(U rhs) if (is(typeof(Checked!(T, Hook)(rhs)))) ref returnAssignment operator. Has the same constraints as the constructor.
U opCast(U, this _)() if (isIntegral!U || isFloatingPoint!U || is(U == bool))Casting operator to integral, `bool`, or floating point type.
bool opEquals(U, this _)(U rhs) if (isIntegral!U || isFloatingPoint!U || is(U == bool) || is(U == Checked!(V, W), V, W) && is(typeof(this == rhs.payload)))Compares `this` against `rhs` for equality.
size_t toHash() const nothrow @safeGenerates a hash for `this`. If `Hook` defines `hookToHash`, the call immediately returns `hook.hookToHash(payload)`. If `Hook` does not implement `hookToHash`, but it has state, a hash will be gen...
size_t toHash(this _)() shared const nothrow @safeditto
void toString(Writer, Char)(scope ref Writer sink, scope const ref FormatSpec!Char fmt) constWrites a string representation of this to a `sink`.
auto opCmp(U, this _)(const U rhs) if (isIntegral!U || isFloatingPoint!U || is(U == bool))Compares `this` against `rhs` for ordering. If `Hook` defines `hookOpCmp`, the function forwards to hook.hookOpCmp(get). Otherwise, the result of the built-in comparison operation is returned.
auto opCmp(U, Hook1, this _)(Checked!(U, Hook1) rhs)ditto
auto opUnary(string op, this _)() if (op == "+" || op == "-" || op == "~")Defines unary operators `+`, `-`, `~`, `++`, and `--`. Unary `+` is not overridable and always has built-in behavior (returns `this`). For the others, if `Hook` defines `hookOpUnary`, `opUnary` for...
Checked opUnary(string op)() if (op == "++" || op == "--") ref returnditto
auto opBinary(string op, Rhs)(const Rhs rhs) if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool))Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`. If `Hook` defines `hookOpBinary`, `opBinary` forwards to Checked!(typeof(hook.hookOpBinary!op(get), Hoo...
auto opBinary(string op, Rhs)(const Rhs rhs) if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) constditto
private auto opBinaryImpl(string op, Rhs, this _)(const Rhs rhs)
auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs)ditto
auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) constditto
private auto opBinaryImpl2(string op, U, Hook1, this _)(Checked!(U, Hook1) rhs)
auto opBinaryRight(string op, Lhs)(const Lhs lhs) if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool))Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>` for the case when a built-in numeric or Boolean type is on the left-hand side, and a `Checked` instance ...
auto opBinaryRight(string op, Lhs)(const Lhs lhs) if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool)) constditto
private auto opBinaryRightImpl(string op, Lhs, this _)(const Lhs lhs)
Checked opOpAssign(string op, Rhs)(const Rhs rhs) if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) ref returnDefines operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.
Checked opOpAssign(string op, Rhs)(const Rhs rhs) if (is(Rhs == Checked!(RhsT, RhsHook), RhsT, RhsHook)) ref returnditto
Constructors
this(U rhs)Constructor taking a value properly convertible to the underlying type. `U` may be either an integral that can be converted to `T` without a loss, or another `Checked` instance whose representation...
this(Range str)Construct from a decimal string. The conversion follows the same rules as to converting a string to the wrapped `T` type.
structAbort

Force all integral errors to fail by printing an error message to stderr and then abort the program. Abort is the default second argument for Checked.

Methods
Dst onBadCast(Dst, Src)(Src src)Called automatically upon a bad cast (one that loses precision or attempts to convert a negative value to an unsigned type). The source type is `Src` and the destination type is `Dst`.
T onLowerBound(Rhs, T)(Rhs rhs, T bound)Called automatically upon a bounds error.
T onUpperBound(Rhs, T)(Rhs rhs, T bound)ditto
bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)Called automatically upon a comparison for equality. In case of a erroneous comparison (one that would make a signed negative value appear equal to an unsigned positive value), this hook issues `as...
int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)Called automatically upon a comparison for ordering using one of the operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. it would make a signed negative value appear greate...
typeof(~ Lhs()) onOverflow(string x, Lhs)(Lhs lhs)Called automatically upon an overflow during a unary or binary operation.
typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)ditto
structThrow

Force all integral errors to fail by throwing an exception of type Throw.CheckFailure. The message coming with the error is similar to the one printed by Warn.

Methods
Dst onBadCast(Dst, Src)(Src src)Called automatically upon a bad cast (one that loses precision or attempts to convert a negative value to an unsigned type). The source type is `Src` and the destination type is `Dst`.
T onLowerBound(Rhs, T)(Rhs rhs, T bound)Called automatically upon a bounds error.
T onUpperBound(Rhs, T)(Rhs rhs, T bound)ditto
bool hookOpEquals(L, R)(L lhs, R rhs)Called automatically upon a comparison for equality. Throws upon an erroneous comparison (one that would make a signed negative value appear equal to an unsigned positive value).
int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)Called automatically upon a comparison for ordering using one of the operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. it would make a signed negative value appear greate...
typeof(~ Lhs()) onOverflow(string x, Lhs)(Lhs lhs)Called automatically upon an overflow during a unary or binary operation.
typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)ditto
Nested Templates
CheckFailureException type thrown upon any failure.
structWarn

Hook that prints to stderr a trace of all integral errors, without affecting default behavior.

Methods
Dst onBadCast(Dst, Src)(Src src)Called automatically upon a bad cast from `src` to type `Dst` (one that loses precision or attempts to convert a negative value to an unsigned type).
T onLowerBound(Rhs, T)(Rhs rhs, T bound)Called automatically upon a bad `opOpAssign` call (one that loses precision or attempts to convert a negative value to an unsigned type).
T onUpperBound(Rhs, T)(Rhs rhs, T bound)ditto
bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)Called automatically upon a comparison for equality. In case of an Erroneous comparison (one that would make a signed negative value appear equal to an unsigned positive value), writes a warning me...
int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)Called automatically upon a comparison for ordering using one of the operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. it would make a signed negative value appear greate...
typeof(~ Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs)Called automatically upon an overflow during a unary or binary operation.
typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)ditto
private @property auto ref trustedStderr() @trusted

Hook that provides arithmetically correct comparisons for equality and ordering. Comparing an object of type Checked!(X, ProperCompare) against another integral (for equality or ordering) ensures that no surprising conversions from signed to unsigned integral occur before the comparison. Using Checked!(X, ProperCompare) on either side of a comparison for equality against a floating-point number makes sure the integral can be properly converted to the floating point type, thus making sure equality is transitive.

Methods
bool hookOpEquals(L, R)(L lhs, R rhs)Hook for `==` and `!=` that ensures comparison against integral values has the behavior expected by the usual arithmetic rules. The built-in semantics yield surprising behavior when comparing signe...
static auto hookOpCmp(L, R)(L lhs, R rhs)Hook for `<`, `<=`, `>`, and `>=` that ensures comparison against integral values has the behavior expected by the usual arithmetic rules. The built-in semantics yield surprising behavior when comp...
structWithNaN

Hook that reserves a special value as a "Not a Number" representative. For signed integrals, the reserved value is T.min. For unsigned integrals, the reserved value is T.max.

The default value of a Checked!(X, WithNaN) is its NaN value, so care must be taken that all variables are explicitly initialized. Any arithmetic and logic operation involving at least on NaN becomes NaN itself. All of a == b, a < b, a > b, a <= b, a >= b yield false if at least one of a and b is NaN.

Fields
T defaultValueThe default value used for values not explicitly initialized. It is the NaN value, i.e. `T.min` for signed integrals and `T.max` for unsigned integrals.
T maxThe maximum value representable is `T.max` for signed integrals, T.max - 1 for unsigned integrals. The minimum value representable is T.min + 1 for signed integrals, `0` for unsigned integrals.
T minditto
Methods
Lhs hookOpCast(Lhs, Rhs)(Rhs rhs)If `rhs` is `WithNaN.defaultValue!Rhs`, returns `WithNaN.defaultValue!Lhs`. Otherwise, returns cast(Lhs rhs).
bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)Returns `false` if lhs == WithNaN.defaultValue!Lhs, lhs == rhs otherwise.
double hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)If lhs == WithNaN.defaultValue!Lhs, returns `double.init`. Otherwise, has the same semantics as the default comparison.
auto hookOpUnary(string x, T)(ref T v)Defines hooks for unary operators `-`, `~`, `++`, and `--`.
auto hookOpBinary(string x, L, R)(L lhs, R rhs)Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the left-hand side operand. If lhs == WithNaN.defaultVal...
auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs)Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the right-hand side operand. If rhs == WithNaN.defaultVa...
void hookOpOpAssign(string x, L, R)(ref L lhs, R rhs)Defines hooks for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=` for cases where a `Checked` object is the left-hand side operand. If lhs == WithNa...
structSaturate

Hook that implements saturation, i.e. any arithmetic operation that would overflow leaves the result at its extreme value (min or max depending on the direction of the overflow).

Saturation is not sticky; if a value reaches its saturation value, another operation may take it back to normal range.

Methods
T onLowerBound(Rhs, T)(Rhs, T bound)Implements saturation for operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`. This hook is called if the result of the binary operation does not fit in `Lhs` ...
T onUpperBound(Rhs, T)(Rhs, T bound)ditto
auto onOverflow(string x, Lhs)(Lhs)Implements saturation for operators `+`, `-` (unary and binary), `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.
typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)ditto

Functions 5

fnChecked!(T, Hook) checked(Hook = Abort, T)(const T value) if (is(typeof(Checked!(T, Hook)(value))))Convenience function that turns an integral into the corresponding `Checked` instance by using template argument deduction. The hook type may be specified (by default `Abort`).
fnbool isNaN(T)(const Checked!(T, WithNaN) x)Queries whether a Checked!(T) object is not a number (NaN).
fntypeof(mixin(x == "cmp" ? "0" : ("L() " ~ x ~ " R()"))) opChecked(string x, L, R)(const L lhs, const R rhs, ref bool overflow) if (isIntegral!L && isIntegral!R)Defines binary operations with overflow checking for any two integral types. The result type obeys the language rules (even when they may be counterintuitive), and `overflow` is set if an overflow ...
private fnpure @safe nothrow @nogc auto pow(L, R)(const L lhs, const R rhs, ref bool overflow) if (isIntegral!L && isIntegral!R)
private fnT powImpl(T)(T b, uint e, ref bool overflow) if (isIntegral!T && T.sizeof >= 4) pure @safe nothrow @nogc