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).
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 occurs (including inadvertent change of signedness, e.g. -1 is converted to uint). Conceptually the behavior is:
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.
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.
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.
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).
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.
Hook that prints to stderr a trace of all integral errors, without affecting default behavior.
Hook that reserves a special value as a "Not a Number" representative. For signed integrals, the reserved value is T.min. For signed integrals, the reserved value is T.max.
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:
Checked!(Checked!int, ProperCompare) |
defines an int with fixed comparison operators that will fail with assert(0) upon overflow. (Recall that Abort is the default policy.) The order in which policies are combined is important because the outermost policy (ProperCompare in this case) has the first crack at intercepting an operator. The converse combination Checked!(Checked!(int, ProperCompare)) is meaningless because Abort will intercept comparison and will fail without giving ProperCompare a chance to intervene. |
Checked!(Checked!(int, ProperCompare), WithNaN) |
defines an int-like type that supports a NaN value. For values that are not NaN, comparison works properly. Again the composition order is important; Checked!(Checked!(int, WithNaN), ProperCompare) does not have good semantics because ProperCompare intercepts comparisons before the numbers involved are tested for NaN. |
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. |
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:
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: