jpayne@69: // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors jpayne@69: // Licensed under the MIT License: jpayne@69: // jpayne@69: // Permission is hereby granted, free of charge, to any person obtaining a copy jpayne@69: // of this software and associated documentation files (the "Software"), to deal jpayne@69: // in the Software without restriction, including without limitation the rights jpayne@69: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell jpayne@69: // copies of the Software, and to permit persons to whom the Software is jpayne@69: // furnished to do so, subject to the following conditions: jpayne@69: // jpayne@69: // The above copyright notice and this permission notice shall be included in jpayne@69: // all copies or substantial portions of the Software. jpayne@69: // jpayne@69: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR jpayne@69: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, jpayne@69: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE jpayne@69: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER jpayne@69: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, jpayne@69: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN jpayne@69: // THE SOFTWARE. jpayne@69: jpayne@69: // This file contains types which are intended to help detect incorrect usage at compile jpayne@69: // time, but should then be optimized down to basic primitives (usually, integers) by the jpayne@69: // compiler. jpayne@69: jpayne@69: #pragma once jpayne@69: jpayne@69: #include "common.h" jpayne@69: #include jpayne@69: jpayne@69: KJ_BEGIN_HEADER jpayne@69: jpayne@69: namespace kj { jpayne@69: jpayne@69: // ======================================================================================= jpayne@69: // IDs jpayne@69: jpayne@69: template jpayne@69: struct Id { jpayne@69: // A type-safe numeric ID. `UnderlyingType` is the underlying integer representation. `Label` jpayne@69: // distinguishes this Id from other Id types. Sample usage: jpayne@69: // jpayne@69: // class Foo; jpayne@69: // typedef Id FooId; jpayne@69: // jpayne@69: // class Bar; jpayne@69: // typedef Id BarId; jpayne@69: // jpayne@69: // You can now use the FooId and BarId types without any possibility of accidentally using a jpayne@69: // FooId when you really wanted a BarId or vice-versa. jpayne@69: jpayne@69: UnderlyingType value; jpayne@69: jpayne@69: inline constexpr Id(): value(0) {} jpayne@69: inline constexpr explicit Id(int value): value(value) {} jpayne@69: jpayne@69: inline constexpr bool operator==(const Id& other) const { return value == other.value; } jpayne@69: inline constexpr bool operator!=(const Id& other) const { return value != other.value; } jpayne@69: inline constexpr bool operator<=(const Id& other) const { return value <= other.value; } jpayne@69: inline constexpr bool operator>=(const Id& other) const { return value >= other.value; } jpayne@69: inline constexpr bool operator< (const Id& other) const { return value < other.value; } jpayne@69: inline constexpr bool operator> (const Id& other) const { return value > other.value; } jpayne@69: }; jpayne@69: jpayne@69: // ======================================================================================= jpayne@69: // Quantity and UnitRatio -- implement unit analysis via the type system jpayne@69: jpayne@69: struct Unsafe_ {}; jpayne@69: constexpr Unsafe_ unsafe = Unsafe_(); jpayne@69: // Use as a parameter to constructors that are unsafe to indicate that you really do mean it. jpayne@69: jpayne@69: template jpayne@69: class Bounded; jpayne@69: template jpayne@69: class BoundedConst; jpayne@69: jpayne@69: template jpayne@69: struct IsIntegralOrBounded_ { static constexpr bool value = isIntegral(); }; jpayne@69: template jpayne@69: struct IsIntegralOrBounded_> { static constexpr bool value = true; }; jpayne@69: template jpayne@69: struct IsIntegralOrBounded_> { static constexpr bool value = true; }; jpayne@69: jpayne@69: template jpayne@69: inline constexpr bool isIntegralOrBounded() { return IsIntegralOrBounded_::value; } jpayne@69: jpayne@69: template jpayne@69: class UnitRatio { jpayne@69: // A multiplier used to convert Quantities of one unit to Quantities of another unit. See jpayne@69: // Quantity, below. jpayne@69: // jpayne@69: // Construct this type by dividing one Quantity by another of a different unit. Use this type jpayne@69: // by multiplying it by a Quantity, or dividing a Quantity by it. jpayne@69: jpayne@69: static_assert(isIntegralOrBounded(), jpayne@69: "Underlying type for UnitRatio must be integer."); jpayne@69: jpayne@69: public: jpayne@69: inline UnitRatio() {} jpayne@69: jpayne@69: constexpr UnitRatio(Number unit1PerUnit2, decltype(unsafe)): unit1PerUnit2(unit1PerUnit2) {} jpayne@69: // This constructor was intended to be private, but GCC complains about it being private in a jpayne@69: // bunch of places that don't appear to even call it, so I made it public. Oh well. jpayne@69: jpayne@69: template jpayne@69: inline constexpr UnitRatio(const UnitRatio& other) jpayne@69: : unit1PerUnit2(other.unit1PerUnit2) {} jpayne@69: jpayne@69: template jpayne@69: inline constexpr UnitRatio jpayne@69: operator+(UnitRatio other) const { jpayne@69: return UnitRatio( jpayne@69: unit1PerUnit2 + other.unit1PerUnit2, unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr UnitRatio jpayne@69: operator-(UnitRatio other) const { jpayne@69: return UnitRatio( jpayne@69: unit1PerUnit2 - other.unit1PerUnit2, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr UnitRatio jpayne@69: operator*(UnitRatio other) const { jpayne@69: // U1 / U2 * U3 / U1 = U3 / U2 jpayne@69: return UnitRatio( jpayne@69: unit1PerUnit2 * other.unit1PerUnit2, unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr UnitRatio jpayne@69: operator*(UnitRatio other) const { jpayne@69: // U1 / U2 * U2 / U3 = U1 / U3 jpayne@69: return UnitRatio( jpayne@69: unit1PerUnit2 * other.unit1PerUnit2, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr UnitRatio jpayne@69: operator/(UnitRatio other) const { jpayne@69: // (U1 / U2) / (U1 / U3) = U3 / U2 jpayne@69: return UnitRatio( jpayne@69: unit1PerUnit2 / other.unit1PerUnit2, unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr UnitRatio jpayne@69: operator/(UnitRatio other) const { jpayne@69: // (U1 / U2) / (U3 / U2) = U1 / U3 jpayne@69: return UnitRatio( jpayne@69: unit1PerUnit2 / other.unit1PerUnit2, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline decltype(Number() / OtherNumber()) jpayne@69: operator/(UnitRatio other) const { jpayne@69: return unit1PerUnit2 / other.unit1PerUnit2; jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr bool operator==(const UnitRatio& other) const { jpayne@69: return unit1PerUnit2 == other.unit1PerUnit2; jpayne@69: } jpayne@69: template jpayne@69: inline constexpr bool operator!=(const UnitRatio& other) const { jpayne@69: return unit1PerUnit2 != other.unit1PerUnit2; jpayne@69: } jpayne@69: jpayne@69: private: jpayne@69: Number unit1PerUnit2; jpayne@69: jpayne@69: template jpayne@69: friend class Quantity; jpayne@69: template jpayne@69: friend class UnitRatio; jpayne@69: jpayne@69: template jpayne@69: friend inline constexpr UnitRatio jpayne@69: operator*(N1, UnitRatio); jpayne@69: }; jpayne@69: jpayne@69: template () && isIntegralOrBounded()>> jpayne@69: inline constexpr UnitRatio jpayne@69: operator*(N1 n, UnitRatio r) { jpayne@69: return UnitRatio(n * r.unit1PerUnit2, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: class Quantity { jpayne@69: // A type-safe numeric quantity, specified in terms of some unit. Two Quantities cannot be used jpayne@69: // in arithmetic unless they use the same unit. The `Unit` type parameter is only used to prevent jpayne@69: // accidental mixing of units; this type is never instantiated and can very well be incomplete. jpayne@69: // `Number` is the underlying primitive numeric type. jpayne@69: // jpayne@69: // Quantities support most basic arithmetic operators, intelligently handling units, and jpayne@69: // automatically casting the underlying type in the same way that the compiler would. jpayne@69: // jpayne@69: // To convert a primitive number to a Quantity, multiply it by unit>(). jpayne@69: // To convert a Quantity to a primitive number, divide it by unit>(). jpayne@69: // To convert a Quantity of one unit to another unit, multiply or divide by a UnitRatio. jpayne@69: // jpayne@69: // The Quantity class is not well-suited to hardcore physics as it does not allow multiplying jpayne@69: // one quantity by another. For example, multiplying meters by meters won't get you square jpayne@69: // meters; it will get you a compiler error. It would be interesting to see if template jpayne@69: // metaprogramming could properly deal with such things but this isn't needed for the present jpayne@69: // use case. jpayne@69: // jpayne@69: // Sample usage: jpayne@69: // jpayne@69: // class SecondsLabel; jpayne@69: // typedef Quantity Seconds; jpayne@69: // constexpr Seconds SECONDS = unit(); jpayne@69: // jpayne@69: // class MinutesLabel; jpayne@69: // typedef Quantity Minutes; jpayne@69: // constexpr Minutes MINUTES = unit(); jpayne@69: // jpayne@69: // constexpr UnitRatio SECONDS_PER_MINUTE = jpayne@69: // 60 * SECONDS / MINUTES; jpayne@69: // jpayne@69: // void waitFor(Seconds seconds) { jpayne@69: // sleep(seconds / SECONDS); jpayne@69: // } jpayne@69: // void waitFor(Minutes minutes) { jpayne@69: // waitFor(minutes * SECONDS_PER_MINUTE); jpayne@69: // } jpayne@69: // jpayne@69: // void waitThreeMinutes() { jpayne@69: // waitFor(3 * MINUTES); jpayne@69: // } jpayne@69: jpayne@69: static_assert(isIntegralOrBounded(), jpayne@69: "Underlying type for Quantity must be integer."); jpayne@69: jpayne@69: public: jpayne@69: inline constexpr Quantity() = default; jpayne@69: jpayne@69: inline constexpr Quantity(MaxValue_): value(maxValue) {} jpayne@69: inline constexpr Quantity(MinValue_): value(minValue) {} jpayne@69: // Allow initialization from maxValue and minValue. jpayne@69: // TODO(msvc): decltype(maxValue) and decltype(minValue) deduce unknown-type for these function jpayne@69: // parameters, causing the compiler to complain of a duplicate constructor definition, so we jpayne@69: // specify MaxValue_ and MinValue_ types explicitly. jpayne@69: jpayne@69: inline constexpr Quantity(Number value, decltype(unsafe)): value(value) {} jpayne@69: // This constructor was intended to be private, but GCC complains about it being private in a jpayne@69: // bunch of places that don't appear to even call it, so I made it public. Oh well. jpayne@69: jpayne@69: template jpayne@69: inline constexpr Quantity(const Quantity& other) jpayne@69: : value(other.value) {} jpayne@69: jpayne@69: template jpayne@69: inline Quantity& operator=(const Quantity& other) { jpayne@69: value = other.value; jpayne@69: return *this; jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Quantity jpayne@69: operator+(const Quantity& other) const { jpayne@69: return Quantity(value + other.value, unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr Quantity jpayne@69: operator-(const Quantity& other) const { jpayne@69: return Quantity(value - other.value, unsafe); jpayne@69: } jpayne@69: template ()>> jpayne@69: inline constexpr Quantity jpayne@69: operator*(OtherNumber other) const { jpayne@69: return Quantity(value * other, unsafe); jpayne@69: } jpayne@69: template ()>> jpayne@69: inline constexpr Quantity jpayne@69: operator/(OtherNumber other) const { jpayne@69: return Quantity(value / other, unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr decltype(Number() / OtherNumber()) jpayne@69: operator/(const Quantity& other) const { jpayne@69: return value / other.value; jpayne@69: } jpayne@69: template jpayne@69: inline constexpr Quantity jpayne@69: operator%(const Quantity& other) const { jpayne@69: return Quantity(value % other.value, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Quantity jpayne@69: operator*(UnitRatio ratio) const { jpayne@69: return Quantity( jpayne@69: value * ratio.unit1PerUnit2, unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr Quantity jpayne@69: operator/(UnitRatio ratio) const { jpayne@69: return Quantity( jpayne@69: value / ratio.unit1PerUnit2, unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr Quantity jpayne@69: operator%(UnitRatio ratio) const { jpayne@69: return Quantity( jpayne@69: value % ratio.unit1PerUnit2, unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr UnitRatio jpayne@69: operator/(Quantity other) const { jpayne@69: return UnitRatio( jpayne@69: value / other.value, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr bool operator==(const Quantity& other) const { jpayne@69: return value == other.value; jpayne@69: } jpayne@69: template jpayne@69: inline constexpr bool operator!=(const Quantity& other) const { jpayne@69: return value != other.value; jpayne@69: } jpayne@69: template jpayne@69: inline constexpr bool operator<=(const Quantity& other) const { jpayne@69: return value <= other.value; jpayne@69: } jpayne@69: template jpayne@69: inline constexpr bool operator>=(const Quantity& other) const { jpayne@69: return value >= other.value; jpayne@69: } jpayne@69: template jpayne@69: inline constexpr bool operator<(const Quantity& other) const { jpayne@69: return value < other.value; jpayne@69: } jpayne@69: template jpayne@69: inline constexpr bool operator>(const Quantity& other) const { jpayne@69: return value > other.value; jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Quantity& operator+=(const Quantity& other) { jpayne@69: value += other.value; jpayne@69: return *this; jpayne@69: } jpayne@69: template jpayne@69: inline Quantity& operator-=(const Quantity& other) { jpayne@69: value -= other.value; jpayne@69: return *this; jpayne@69: } jpayne@69: template jpayne@69: inline Quantity& operator*=(OtherNumber other) { jpayne@69: value *= other; jpayne@69: return *this; jpayne@69: } jpayne@69: template jpayne@69: inline Quantity& operator/=(OtherNumber other) { jpayne@69: value /= other.value; jpayne@69: return *this; jpayne@69: } jpayne@69: jpayne@69: private: jpayne@69: Number value; jpayne@69: jpayne@69: template jpayne@69: friend class Quantity; jpayne@69: jpayne@69: template jpayne@69: friend inline constexpr auto operator*(Number1 a, Quantity b) jpayne@69: -> Quantity; jpayne@69: }; jpayne@69: jpayne@69: template struct Unit_ { jpayne@69: static inline constexpr T get() { return T(1); } jpayne@69: }; jpayne@69: template jpayne@69: struct Unit_> { jpayne@69: static inline constexpr Quantity::get()), U> get() { jpayne@69: return Quantity::get()), U>(Unit_::get(), unsafe); jpayne@69: } jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: inline constexpr auto unit() -> decltype(Unit_::get()) { return Unit_::get(); } jpayne@69: // unit>() returns a Quantity of value 1. It also, intentionally, works on basic jpayne@69: // numeric types. jpayne@69: jpayne@69: template ()>> jpayne@69: inline constexpr auto operator*(Number1 a, Quantity b) jpayne@69: -> Quantity { jpayne@69: return Quantity(a * b.value, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr auto operator*(UnitRatio ratio, jpayne@69: Quantity measure) jpayne@69: -> decltype(measure * ratio) { jpayne@69: return measure * ratio; jpayne@69: } jpayne@69: jpayne@69: // ======================================================================================= jpayne@69: // Absolute measures jpayne@69: jpayne@69: template jpayne@69: class Absolute { jpayne@69: // Wraps some other value -- typically a Quantity -- but represents a value measured based on jpayne@69: // some absolute origin. For example, if `Duration` is a type representing a time duration, jpayne@69: // Absolute might be a calendar date. jpayne@69: // jpayne@69: // Since Absolute represents measurements relative to some arbitrary origin, the only sensible jpayne@69: // arithmetic to perform on them is addition and subtraction. jpayne@69: jpayne@69: // TODO(someday): Do the same automatic expansion of integer width that Quantity does? Doesn't jpayne@69: // matter for our time use case, where we always use 64-bit anyway. Note that fixing this jpayne@69: // would implicitly allow things like multiplying an Absolute by a UnitRatio to change its jpayne@69: // units, which is actually totally logical and kind of neat. jpayne@69: jpayne@69: public: jpayne@69: inline constexpr Absolute(MaxValue_): value(maxValue) {} jpayne@69: inline constexpr Absolute(MinValue_): value(minValue) {} jpayne@69: // Allow initialization from maxValue and minValue. jpayne@69: // TODO(msvc): decltype(maxValue) and decltype(minValue) deduce unknown-type for these function jpayne@69: // parameters, causing the compiler to complain of a duplicate constructor definition, so we jpayne@69: // specify MaxValue_ and MinValue_ types explicitly. jpayne@69: jpayne@69: inline constexpr Absolute operator+(const T& other) const { return Absolute(value + other); } jpayne@69: inline constexpr Absolute operator-(const T& other) const { return Absolute(value - other); } jpayne@69: inline constexpr T operator-(const Absolute& other) const { return value - other.value; } jpayne@69: jpayne@69: inline Absolute& operator+=(const T& other) { value += other; return *this; } jpayne@69: inline Absolute& operator-=(const T& other) { value -= other; return *this; } jpayne@69: jpayne@69: inline constexpr bool operator==(const Absolute& other) const { return value == other.value; } jpayne@69: inline constexpr bool operator!=(const Absolute& other) const { return value != other.value; } jpayne@69: inline constexpr bool operator<=(const Absolute& other) const { return value <= other.value; } jpayne@69: inline constexpr bool operator>=(const Absolute& other) const { return value >= other.value; } jpayne@69: inline constexpr bool operator< (const Absolute& other) const { return value < other.value; } jpayne@69: inline constexpr bool operator> (const Absolute& other) const { return value > other.value; } jpayne@69: jpayne@69: private: jpayne@69: T value; jpayne@69: jpayne@69: explicit constexpr Absolute(T value): value(value) {} jpayne@69: jpayne@69: template jpayne@69: friend inline constexpr U origin(); jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: inline constexpr Absolute operator+(const T& a, const Absolute& b) { jpayne@69: return b + a; jpayne@69: } jpayne@69: jpayne@69: template struct UnitOf_ { typedef T Type; }; jpayne@69: template struct UnitOf_> { typedef T Type; }; jpayne@69: template jpayne@69: using UnitOf = typename UnitOf_::Type; jpayne@69: // UnitOf> is T. UnitOf is AnythingElse. jpayne@69: jpayne@69: template jpayne@69: inline constexpr T origin() { return T(0 * unit>()); } jpayne@69: // origin>() returns an Absolute of value 0. It also, intentionally, works on basic jpayne@69: // numeric types. jpayne@69: jpayne@69: // ======================================================================================= jpayne@69: // Overflow avoidance jpayne@69: jpayne@69: template jpayne@69: struct BitCount_ { jpayne@69: static constexpr uint value = BitCount_<(n >> 1), accum + 1>::value; jpayne@69: }; jpayne@69: template jpayne@69: struct BitCount_<0, accum> { jpayne@69: static constexpr uint value = accum; jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: inline constexpr uint bitCount() { return BitCount_::value; } jpayne@69: // Number of bits required to represent the number `n`. jpayne@69: jpayne@69: template struct AtLeastUInt_ { jpayne@69: static_assert(bitCountBitCount < 7, "don't know how to represent integers over 64 bits"); jpayne@69: }; jpayne@69: template <> struct AtLeastUInt_<0> { typedef uint8_t Type; }; jpayne@69: template <> struct AtLeastUInt_<1> { typedef uint8_t Type; }; jpayne@69: template <> struct AtLeastUInt_<2> { typedef uint8_t Type; }; jpayne@69: template <> struct AtLeastUInt_<3> { typedef uint8_t Type; }; jpayne@69: template <> struct AtLeastUInt_<4> { typedef uint16_t Type; }; jpayne@69: template <> struct AtLeastUInt_<5> { typedef uint32_t Type; }; jpayne@69: template <> struct AtLeastUInt_<6> { typedef uint64_t Type; }; jpayne@69: jpayne@69: template jpayne@69: using AtLeastUInt = typename AtLeastUInt_()>::Type; jpayne@69: // AtLeastUInt is an unsigned integer of at least n bits. E.g. AtLeastUInt<12> is uint16_t. jpayne@69: jpayne@69: // ------------------------------------------------------------------- jpayne@69: jpayne@69: template jpayne@69: class BoundedConst { jpayne@69: // A constant integer value on which we can do bit size analysis. jpayne@69: jpayne@69: public: jpayne@69: BoundedConst() = default; jpayne@69: jpayne@69: inline constexpr uint unwrap() const { return value; } jpayne@69: jpayne@69: #define OP(op, check) \ jpayne@69: template \ jpayne@69: inline constexpr BoundedConst<(value op other)> \ jpayne@69: operator op(BoundedConst) const { \ jpayne@69: static_assert(check, "overflow in BoundedConst arithmetic"); \ jpayne@69: return BoundedConst<(value op other)>(); \ jpayne@69: } jpayne@69: #define COMPARE_OP(op) \ jpayne@69: template \ jpayne@69: inline constexpr bool operator op(BoundedConst) const { \ jpayne@69: return value op other; \ jpayne@69: } jpayne@69: jpayne@69: OP(+, value + other >= value) jpayne@69: OP(-, value - other <= value) jpayne@69: OP(*, value * other / other == value) jpayne@69: OP(/, true) // div by zero already errors out; no other division ever overflows jpayne@69: OP(%, true) // mod by zero already errors out; no other modulus ever overflows jpayne@69: OP(<<, value << other >= value) jpayne@69: OP(>>, true) // right shift can't overflow jpayne@69: OP(&, true) // bitwise ops can't overflow jpayne@69: OP(|, true) // bitwise ops can't overflow jpayne@69: jpayne@69: COMPARE_OP(==) jpayne@69: COMPARE_OP(!=) jpayne@69: COMPARE_OP(< ) jpayne@69: COMPARE_OP(> ) jpayne@69: COMPARE_OP(<=) jpayne@69: COMPARE_OP(>=) jpayne@69: #undef OP jpayne@69: #undef COMPARE_OP jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: struct Unit_> { jpayne@69: static inline constexpr BoundedConst<1> get() { return BoundedConst<1>(); } jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: struct Unit_> { jpayne@69: static inline constexpr BoundedConst<1> get() { return BoundedConst<1>(); } jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: inline constexpr BoundedConst bounded() { jpayne@69: return BoundedConst(); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: static constexpr uint64_t boundedAdd() { jpayne@69: static_assert(a + b >= a, "possible overflow detected"); jpayne@69: return a + b; jpayne@69: } jpayne@69: template jpayne@69: static constexpr uint64_t boundedSub() { jpayne@69: static_assert(a - b <= a, "possible underflow detected"); jpayne@69: return a - b; jpayne@69: } jpayne@69: template jpayne@69: static constexpr uint64_t boundedMul() { jpayne@69: static_assert(a * b / b == a, "possible overflow detected"); jpayne@69: return a * b; jpayne@69: } jpayne@69: template jpayne@69: static constexpr uint64_t boundedLShift() { jpayne@69: static_assert(a << b >= a, "possible overflow detected"); jpayne@69: return a << b; jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr BoundedConst min(BoundedConst, BoundedConst) { jpayne@69: return bounded(); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr BoundedConst max(BoundedConst, BoundedConst) { jpayne@69: return bounded(); jpayne@69: } jpayne@69: // We need to override min() and max() between constants because the ternary operator in the jpayne@69: // default implementation would complain. jpayne@69: jpayne@69: // ------------------------------------------------------------------- jpayne@69: jpayne@69: template jpayne@69: class Bounded { jpayne@69: public: jpayne@69: static_assert(maxN <= T(kj::maxValue), "possible overflow detected"); jpayne@69: jpayne@69: Bounded() = default; jpayne@69: jpayne@69: Bounded(const Bounded& other) = default; jpayne@69: template ()>> jpayne@69: inline constexpr Bounded(OtherInt value): value(value) { jpayne@69: static_assert(OtherInt(maxValue) <= maxN, "possible overflow detected"); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr Bounded(const Bounded& other) jpayne@69: : value(other.value) { jpayne@69: static_assert(otherMax <= maxN, "possible overflow detected"); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr Bounded(BoundedConst) jpayne@69: : value(otherValue) { jpayne@69: static_assert(otherValue <= maxN, "overflow detected"); jpayne@69: } jpayne@69: jpayne@69: Bounded& operator=(const Bounded& other) = default; jpayne@69: template ()>> jpayne@69: Bounded& operator=(OtherInt other) { jpayne@69: static_assert(OtherInt(maxValue) <= maxN, "possible overflow detected"); jpayne@69: value = other; jpayne@69: return *this; jpayne@69: } jpayne@69: template jpayne@69: inline Bounded& operator=(const Bounded& other) { jpayne@69: static_assert(otherMax <= maxN, "possible overflow detected"); jpayne@69: value = other.value; jpayne@69: return *this; jpayne@69: } jpayne@69: template jpayne@69: inline Bounded& operator=(BoundedConst) { jpayne@69: static_assert(otherValue <= maxN, "overflow detected"); jpayne@69: value = otherValue; jpayne@69: return *this; jpayne@69: } jpayne@69: jpayne@69: inline constexpr T unwrap() const { return value; } jpayne@69: jpayne@69: #define OP(op, newMax) \ jpayne@69: template \ jpayne@69: inline constexpr Bounded \ jpayne@69: operator op(const Bounded& other) const { \ jpayne@69: return Bounded(value op other.value, unsafe); \ jpayne@69: } jpayne@69: #define COMPARE_OP(op) \ jpayne@69: template \ jpayne@69: inline constexpr bool operator op(const Bounded& other) const { \ jpayne@69: return value op other.value; \ jpayne@69: } jpayne@69: jpayne@69: OP(+, (boundedAdd())) jpayne@69: OP(*, (boundedMul())) jpayne@69: OP(/, maxN) jpayne@69: OP(%, otherMax - 1) jpayne@69: jpayne@69: // operator- is intentionally omitted because we mostly use this with unsigned types, and jpayne@69: // subtraction requires proof that subtrahend is not greater than the minuend. jpayne@69: jpayne@69: COMPARE_OP(==) jpayne@69: COMPARE_OP(!=) jpayne@69: COMPARE_OP(< ) jpayne@69: COMPARE_OP(> ) jpayne@69: COMPARE_OP(<=) jpayne@69: COMPARE_OP(>=) jpayne@69: jpayne@69: #undef OP jpayne@69: #undef COMPARE_OP jpayne@69: jpayne@69: template jpayne@69: inline Bounded assertMax(ErrorFunc&& func) const { jpayne@69: // Assert that the number is no more than `newMax`. Otherwise, call `func`. jpayne@69: static_assert(newMax < maxN, "this bounded size assertion is redundant"); jpayne@69: if (KJ_UNLIKELY(value > newMax)) func(); jpayne@69: return Bounded(value, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Bounded subtractChecked( jpayne@69: const Bounded& other, ErrorFunc&& func) const { jpayne@69: // Subtract a number, calling func() if the result would underflow. jpayne@69: if (KJ_UNLIKELY(value < other.value)) func(); jpayne@69: return Bounded(value - other.value, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Bounded subtractChecked( jpayne@69: BoundedConst, ErrorFunc&& func) const { jpayne@69: // Subtract a number, calling func() if the result would underflow. jpayne@69: static_assert(otherValue <= maxN, "underflow detected"); jpayne@69: if (KJ_UNLIKELY(value < otherValue)) func(); jpayne@69: return Bounded(value - otherValue, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Maybe> trySubtract( jpayne@69: const Bounded& other) const { jpayne@69: // Subtract a number, calling func() if the result would underflow. jpayne@69: if (value < other.value) { jpayne@69: return nullptr; jpayne@69: } else { jpayne@69: return Bounded(value - other.value, unsafe); jpayne@69: } jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Maybe> trySubtract(BoundedConst) const { jpayne@69: // Subtract a number, calling func() if the result would underflow. jpayne@69: if (value < otherValue) { jpayne@69: return nullptr; jpayne@69: } else { jpayne@69: return Bounded(value - otherValue, unsafe); jpayne@69: } jpayne@69: } jpayne@69: jpayne@69: inline constexpr Bounded(T value, decltype(unsafe)): value(value) {} jpayne@69: template jpayne@69: inline constexpr Bounded(Bounded value, decltype(unsafe)) jpayne@69: : value(value.value) {} jpayne@69: // Mainly for internal use. jpayne@69: // jpayne@69: // Only use these as a last resort, with ample commentary on why you think it's safe. jpayne@69: jpayne@69: private: jpayne@69: T value; jpayne@69: jpayne@69: template jpayne@69: friend class Bounded; jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded bounded(Number value) { jpayne@69: return Bounded(value, unsafe); jpayne@69: } jpayne@69: jpayne@69: inline constexpr Bounded<1, uint8_t> bounded(bool value) { jpayne@69: return Bounded<1, uint8_t>(value, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded(), Number> assumeBits(Number value) { jpayne@69: return Bounded(), Number>(value, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded(), T> assumeBits(Bounded value) { jpayne@69: return Bounded(), T>(value, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr auto assumeBits(Quantity value) jpayne@69: -> Quantity(value / unit>())), Unit> { jpayne@69: return Quantity(value / unit>())), Unit>( jpayne@69: assumeBits(value / unit>()), unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded assumeMax(Number value) { jpayne@69: return Bounded(value, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded assumeMax(Bounded value) { jpayne@69: return Bounded(value, unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr auto assumeMax(Quantity value) jpayne@69: -> Quantity(value / unit>())), Unit> { jpayne@69: return Quantity(value / unit>())), Unit>( jpayne@69: assumeMax(value / unit>()), unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded assumeMax(BoundedConst, Number value) { jpayne@69: return assumeMax(value); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded assumeMax(BoundedConst, Bounded value) { jpayne@69: return assumeMax(value); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr auto assumeMax(Quantity, Unit>, Quantity value) jpayne@69: -> decltype(assumeMax(value)) { jpayne@69: return assumeMax(value); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Bounded assertMax(Bounded value, ErrorFunc&& errorFunc) { jpayne@69: // Assert that the bounded value is less than or equal to the given maximum, calling errorFunc() jpayne@69: // if not. jpayne@69: static_assert(newMax < maxN, "this bounded size assertion is redundant"); jpayne@69: return value.template assertMax(kj::fwd(errorFunc)); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Quantity, Unit> assertMax( jpayne@69: Quantity, Unit> value, ErrorFunc&& errorFunc) { jpayne@69: // Assert that the bounded value is less than or equal to the given maximum, calling errorFunc() jpayne@69: // if not. jpayne@69: static_assert(newMax < maxN, "this bounded size assertion is redundant"); jpayne@69: return (value / unit()).template assertMax( jpayne@69: kj::fwd(errorFunc)) * unit(); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Bounded assertMax( jpayne@69: BoundedConst, Bounded value, ErrorFunc&& errorFunc) { jpayne@69: return assertMax(value, kj::mv(errorFunc)); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Quantity, Unit> assertMax( jpayne@69: Quantity, Unit>, jpayne@69: Quantity, Unit> value, ErrorFunc&& errorFunc) { jpayne@69: return assertMax(value, kj::mv(errorFunc)); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Bounded(), T> assertMaxBits( jpayne@69: Bounded value, ErrorFunc&& errorFunc = ErrorFunc()) { jpayne@69: // Assert that the bounded value requires no more than the given number of bits, calling jpayne@69: // errorFunc() if not. jpayne@69: return assertMax()>(value, kj::fwd(errorFunc)); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline Quantity(), T>, Unit> assertMaxBits( jpayne@69: Quantity, Unit> value, ErrorFunc&& errorFunc = ErrorFunc()) { jpayne@69: // Assert that the bounded value requires no more than the given number of bits, calling jpayne@69: // errorFunc() if not. jpayne@69: return assertMax()>(value, kj::fwd(errorFunc)); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded upgradeBound(Bounded value) { jpayne@69: return value; jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Quantity, Unit> upgradeBound( jpayne@69: Quantity, Unit> value) { jpayne@69: return value; jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline auto subtractChecked(Bounded value, Other other, ErrorFunc&& errorFunc) jpayne@69: -> decltype(value.subtractChecked(other, kj::fwd(errorFunc))) { jpayne@69: return value.subtractChecked(other, kj::fwd(errorFunc)); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline auto subtractChecked(Quantity value, Quantity other, ErrorFunc&& errorFunc) jpayne@69: -> Quantity(errorFunc))), Unit> { jpayne@69: return subtractChecked(value / unit>(), jpayne@69: other / unit>(), jpayne@69: kj::fwd(errorFunc)) jpayne@69: * unit>(); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline auto trySubtract(Bounded value, Other other) jpayne@69: -> decltype(value.trySubtract(other)) { jpayne@69: return value.trySubtract(other); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline auto trySubtract(Quantity value, Quantity other) jpayne@69: -> Maybe> { jpayne@69: return trySubtract(value / unit>(), jpayne@69: other / unit>()) jpayne@69: .map([](decltype(subtractChecked(T(), U(), int())) x) { jpayne@69: return x * unit>(); jpayne@69: }); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded> jpayne@69: min(Bounded a, Bounded b) { jpayne@69: return Bounded>(kj::min(a.unwrap(), b.unwrap()), unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr Bounded> jpayne@69: max(Bounded a, Bounded b) { jpayne@69: return Bounded>(kj::max(a.unwrap(), b.unwrap()), unsafe); jpayne@69: } jpayne@69: // We need to override min() and max() because: jpayne@69: // 1) WiderType<> might not choose the correct bounds. jpayne@69: // 2) One of the two sides of the ternary operator in the default implementation would fail to jpayne@69: // typecheck even though it is OK in practice. jpayne@69: jpayne@69: // ------------------------------------------------------------------- jpayne@69: // Operators between Bounded and BoundedConst jpayne@69: jpayne@69: #define OP(op, newMax) \ jpayne@69: template \ jpayne@69: inline constexpr Bounded<(newMax), decltype(T() op uint())> operator op( \ jpayne@69: Bounded value, BoundedConst) { \ jpayne@69: return Bounded<(newMax), decltype(T() op uint())>(value.unwrap() op cvalue, unsafe); \ jpayne@69: } jpayne@69: jpayne@69: #define REVERSE_OP(op, newMax) \ jpayne@69: template \ jpayne@69: inline constexpr Bounded<(newMax), decltype(uint() op T())> operator op( \ jpayne@69: BoundedConst, Bounded value) { \ jpayne@69: return Bounded<(newMax), decltype(uint() op T())>(cvalue op value.unwrap(), unsafe); \ jpayne@69: } jpayne@69: jpayne@69: #define COMPARE_OP(op) \ jpayne@69: template \ jpayne@69: inline constexpr bool operator op(Bounded value, BoundedConst) { \ jpayne@69: return value.unwrap() op cvalue; \ jpayne@69: } \ jpayne@69: template \ jpayne@69: inline constexpr bool operator op(BoundedConst, Bounded value) { \ jpayne@69: return cvalue op value.unwrap(); \ jpayne@69: } jpayne@69: jpayne@69: OP(+, (boundedAdd())) jpayne@69: REVERSE_OP(+, (boundedAdd())) jpayne@69: jpayne@69: OP(*, (boundedMul())) jpayne@69: REVERSE_OP(*, (boundedAdd())) jpayne@69: jpayne@69: OP(/, maxN / cvalue) jpayne@69: REVERSE_OP(/, cvalue) // denominator could be 1 jpayne@69: jpayne@69: OP(%, cvalue - 1) jpayne@69: REVERSE_OP(%, maxN - 1) jpayne@69: jpayne@69: OP(<<, (boundedLShift())) jpayne@69: REVERSE_OP(<<, (boundedLShift())) jpayne@69: jpayne@69: OP(>>, maxN >> cvalue) jpayne@69: REVERSE_OP(>>, cvalue >> maxN) jpayne@69: jpayne@69: OP(&, maxValueForBits()>() & cvalue) jpayne@69: REVERSE_OP(&, maxValueForBits()>() & cvalue) jpayne@69: jpayne@69: OP(|, maxN | cvalue) jpayne@69: REVERSE_OP(|, maxN | cvalue) jpayne@69: jpayne@69: COMPARE_OP(==) jpayne@69: COMPARE_OP(!=) jpayne@69: COMPARE_OP(< ) jpayne@69: COMPARE_OP(> ) jpayne@69: COMPARE_OP(<=) jpayne@69: COMPARE_OP(>=) jpayne@69: jpayne@69: #undef OP jpayne@69: #undef REVERSE_OP jpayne@69: #undef COMPARE_OP jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded jpayne@69: operator-(BoundedConst, Bounded value) { jpayne@69: // We allow subtraction of a variable from a constant only if the constant is greater than or jpayne@69: // equal to the maximum possible value of the variable. Since the variable could be zero, the jpayne@69: // result can be as large as the constant. jpayne@69: // jpayne@69: // We do not allow subtraction of a constant from a variable because there's never a guarantee it jpayne@69: // won't underflow (unless the constant is zero, which is silly). jpayne@69: static_assert(cvalue >= maxN, "possible underflow detected"); jpayne@69: return Bounded(cvalue - value.unwrap(), unsafe); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Bounded min(Bounded a, BoundedConst) { jpayne@69: return Bounded(kj::min(b, a.unwrap()), unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr Bounded min(BoundedConst, Bounded a) { jpayne@69: return Bounded(kj::min(a.unwrap(), b), unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr Bounded max(Bounded a, BoundedConst) { jpayne@69: return Bounded(kj::max(b, a.unwrap()), unsafe); jpayne@69: } jpayne@69: template jpayne@69: inline constexpr Bounded max(BoundedConst, Bounded a) { jpayne@69: return Bounded(kj::max(a.unwrap(), b), unsafe); jpayne@69: } jpayne@69: // We need to override min() between a Bounded and a constant since: jpayne@69: // 1) WiderType<> might choose BoundedConst over a 1-byte Bounded, which is wrong. jpayne@69: // 2) To clamp the bounds of the output type. jpayne@69: // 3) Same ternary operator typechecking issues. jpayne@69: jpayne@69: // ------------------------------------------------------------------- jpayne@69: jpayne@69: template jpayne@69: class SafeUnwrapper { jpayne@69: public: jpayne@69: inline explicit constexpr SafeUnwrapper(Bounded value): value(value.unwrap()) {} jpayne@69: jpayne@69: template ()>> jpayne@69: inline constexpr operator U() const { jpayne@69: static_assert(maxN <= U(maxValue), "possible truncation detected"); jpayne@69: return value; jpayne@69: } jpayne@69: jpayne@69: inline constexpr operator bool() const { jpayne@69: static_assert(maxN <= 1, "possible truncation detected"); jpayne@69: return value; jpayne@69: } jpayne@69: jpayne@69: private: jpayne@69: T value; jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: inline constexpr SafeUnwrapper unbound(Bounded bounded) { jpayne@69: // Unwraps the bounded value, returning a value that can be implicitly cast to any integer type. jpayne@69: // If this implicit cast could truncate, a compile-time error will be raised. jpayne@69: return SafeUnwrapper(bounded); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: class SafeConstUnwrapper { jpayne@69: public: jpayne@69: template ()>> jpayne@69: inline constexpr operator T() const { jpayne@69: static_assert(value <= T(maxValue), "this operation will truncate"); jpayne@69: return value; jpayne@69: } jpayne@69: jpayne@69: inline constexpr operator bool() const { jpayne@69: static_assert(value <= 1, "this operation will truncate"); jpayne@69: return value; jpayne@69: } jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: inline constexpr SafeConstUnwrapper unbound(BoundedConst) { jpayne@69: return SafeConstUnwrapper(); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr T unboundAs(U value) { jpayne@69: return unbound(value); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr T unboundMax(Bounded value) { jpayne@69: // Explicitly unguard expecting a value that is at most `maxN`. jpayne@69: static_assert(maxN <= requestedMax, "possible overflow detected"); jpayne@69: return value.unwrap(); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr uint unboundMax(BoundedConst) { jpayne@69: // Explicitly unguard expecting a value that is at most `maxN`. jpayne@69: static_assert(value <= requestedMax, "overflow detected"); jpayne@69: return value; jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr auto unboundMaxBits(T value) -> jpayne@69: decltype(unboundMax()>(value)) { jpayne@69: // Explicitly unguard expecting a value that fits into `bits` bits. jpayne@69: return unboundMax()>(value); jpayne@69: } jpayne@69: jpayne@69: #define OP(op) \ jpayne@69: template \ jpayne@69: inline constexpr auto operator op(T a, SafeUnwrapper b) -> decltype(a op (T)b) { \ jpayne@69: return a op (AtLeastUInt)b; \ jpayne@69: } \ jpayne@69: template \ jpayne@69: inline constexpr auto operator op(SafeUnwrapper b, T a) -> decltype((T)b op a) { \ jpayne@69: return (AtLeastUInt)b op a; \ jpayne@69: } \ jpayne@69: template \ jpayne@69: inline constexpr auto operator op(T a, SafeConstUnwrapper b) -> decltype(a op (T)b) { \ jpayne@69: return a op (AtLeastUInt)b; \ jpayne@69: } \ jpayne@69: template \ jpayne@69: inline constexpr auto operator op(SafeConstUnwrapper b, T a) -> decltype((T)b op a) { \ jpayne@69: return (AtLeastUInt)b op a; \ jpayne@69: } jpayne@69: jpayne@69: OP(+) jpayne@69: OP(-) jpayne@69: OP(*) jpayne@69: OP(/) jpayne@69: OP(%) jpayne@69: OP(<<) jpayne@69: OP(>>) jpayne@69: OP(&) jpayne@69: OP(|) jpayne@69: OP(==) jpayne@69: OP(!=) jpayne@69: OP(<=) jpayne@69: OP(>=) jpayne@69: OP(<) jpayne@69: OP(>) jpayne@69: jpayne@69: #undef OP jpayne@69: jpayne@69: // ------------------------------------------------------------------- jpayne@69: jpayne@69: template jpayne@69: class Range> { jpayne@69: public: jpayne@69: inline constexpr Range(Bounded begin, Bounded end) jpayne@69: : inner(unbound(begin), unbound(end)) {} jpayne@69: inline explicit constexpr Range(Bounded end) jpayne@69: : inner(unbound(end)) {} jpayne@69: jpayne@69: class Iterator { jpayne@69: public: jpayne@69: Iterator() = default; jpayne@69: inline explicit Iterator(typename Range::Iterator inner): inner(inner) {} jpayne@69: jpayne@69: inline Bounded operator* () const { return Bounded(*inner, unsafe); } jpayne@69: inline Iterator& operator++() { ++inner; return *this; } jpayne@69: jpayne@69: inline bool operator==(const Iterator& other) const { return inner == other.inner; } jpayne@69: inline bool operator!=(const Iterator& other) const { return inner != other.inner; } jpayne@69: jpayne@69: private: jpayne@69: typename Range::Iterator inner; jpayne@69: }; jpayne@69: jpayne@69: inline Iterator begin() const { return Iterator(inner.begin()); } jpayne@69: inline Iterator end() const { return Iterator(inner.end()); } jpayne@69: jpayne@69: private: jpayne@69: Range inner; jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: class Range> { jpayne@69: public: jpayne@69: inline constexpr Range(Quantity begin, Quantity end) jpayne@69: : inner(begin / unit>(), end / unit>()) {} jpayne@69: inline explicit constexpr Range(Quantity end) jpayne@69: : inner(end / unit>()) {} jpayne@69: jpayne@69: class Iterator { jpayne@69: public: jpayne@69: Iterator() = default; jpayne@69: inline explicit Iterator(typename Range::Iterator inner): inner(inner) {} jpayne@69: jpayne@69: inline Quantity operator* () const { return *inner * unit>(); } jpayne@69: inline Iterator& operator++() { ++inner; return *this; } jpayne@69: jpayne@69: inline bool operator==(const Iterator& other) const { return inner == other.inner; } jpayne@69: inline bool operator!=(const Iterator& other) const { return inner != other.inner; } jpayne@69: jpayne@69: private: jpayne@69: typename Range::Iterator inner; jpayne@69: }; jpayne@69: jpayne@69: inline Iterator begin() const { return Iterator(inner.begin()); } jpayne@69: inline Iterator end() const { return Iterator(inner.end()); } jpayne@69: jpayne@69: private: jpayne@69: Range inner; jpayne@69: }; jpayne@69: jpayne@69: template jpayne@69: inline constexpr Range> zeroTo(BoundedConst end) { jpayne@69: return Range>(end); jpayne@69: } jpayne@69: jpayne@69: template jpayne@69: inline constexpr Range, Unit>> jpayne@69: zeroTo(Quantity, Unit> end) { jpayne@69: return Range, Unit>>(end); jpayne@69: } jpayne@69: jpayne@69: } // namespace kj jpayne@69: jpayne@69: KJ_END_HEADER