phobos ~master (2023-10-10T16:11:09Z)

- checked
`Checked!(T, Hook)`checked(T 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`).- isNaN
`bool`isNaN(Checked!(T, WithNaN) x) - opChecked
`typeof(mixin (x == "cmp" ? "0" : ("L() " ~ x ~ " R()")))`opChecked(L lhs, R rhs, bool overflow) 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:

- Abortstruct Abort
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`.- Checkedstruct Checked(T, Hook = Abort)
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`.- ProperComparestruct ProperCompare
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.- Saturatestruct Saturate
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).- Throwstruct Throw
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`.- Warnstruct Warn
Hook that prints to

`stderr`a trace of all integral errors, without affecting default behavior.- WithNaNstruct WithNaN
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 integralsxandyand setsoverflowtotrueif an overflow occurred. The flagoverflow(aboolpassed 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!intis a type that behaves likeintbut 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 valuexof integral typeTtoChecked!Tby means ofchecked(x). For example:Similarly,

checked(-1) > uint(0)aborts execution (even though the built-in comparisonint(-1) > uint(0)is surprisingly true due to language's conversion rules modeled after C). Thus,Checked!intis a virtually drop-in replacement forintuseable in debug builds, to be replaced byintin release mode if efficiency demands it.Checkedhas customizable behavior with the help of a second type parameter,Hook. Depending on what methodsHookdefines, core operations on the underlying integral may be verified for overflow or completely redefined. IfHookdefines no method at all and carries no state, there is no change in behavior, i.e.Checked!(int, void)is a wrapper aroundintthat adds no customization at all.This module provides a few predefined hooks (below) that add useful behavior to

Checked:assert(0). It is the default second parameter, i.e.Checked!shortis the same asChecked!(short, Abort).==,!=,<,<=,>, and>=to return correct results in all circumstances, at a slight cost in efficiency. For example,Checked!(uint, ProperCompare)(1) > -1istrue, which is not the case for the built-in comparison. Also, comparing numbers for equality with floating-point numbers only passes if the integral can be converted to the floating-point number precisely, so as to preserve transitivity of equality.Checked!(X, WithNaN)gets this special value, it preserves and propagates it until reassigned. isNaN can be used to query whether the object is not a number.Checked!(int, Saturate)"stops" atint.maxfor all operations that would cause anintto overflow toward infinity, and atint.minfor all operations that would correspondingly overflow toward negative infinity.