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 |
onBadCast | If defined and hookOpCast is not defined,
|
hookOpEquals | If defined, hook.hookOpEquals(get, rhs) is
forwarded to unconditionally when the payload is compared for equality against value |
hookOpCmp | If defined, hook.hookOpCmp(get, rhs) is
forwarded to unconditionally when the payload is compared for ordering against value |
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 `--`, |
hookOpBinary | If defined, hook.hookOpBinary!op(get, rhs)
(where |
hookOpBinaryRight | If defined, hook.hookOpBinaryRight!op(lhs, get) (where op is the operator symbol and
|
onOverflow | If defined, hook.onOverflow!op(get) is forwarded
to for unary operators that overflow but only if |
hookOpOpAssign | If defined, hook.hookOpOpAssign!op(payload,
(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 |
onUpperBound | If defined, hook.onUpperBound(value, bound)
(where |
hookToHash | If defined, hook.hookToHash(payload)
(where |
Source: std/checkedint.d
Types 7
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
T | type that is wrapped in the Checked type |
Hook | hook type that customizes the behavior of the Checked type |
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...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 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...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)) constdittoauto opBinaryImpl(string op, Rhs, this _)(const Rhs rhs)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)) constdittoauto 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 returndittoForce 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.
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)dittobool 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)dittoForce 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.
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)dittobool 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)dittoCheckFailureException type thrown upon any failure.Hook that prints to stderr a trace of all integral errors, without affecting default behavior.
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)dittobool 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)dittoHook 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.
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...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.
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 mindittoLhs 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...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.
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)dittoauto 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)dittoFunctions 5
Checked!(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`).bool isNaN(T)(const Checked!(T, WithNaN) x)Queries whether a Checked!(T) object is not a number (NaN).typeof(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 ...pure @safe nothrow @nogc auto pow(L, R)(const L lhs, const R rhs, ref bool overflow) if (isIntegral!L && isIntegral!R)T powImpl(T)(T b, uint e, ref bool overflow) if (isIntegral!T && T.sizeof >= 4) pure @safe nothrow @nogc