OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_ |
6 #define BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_H_ |
7 | 7 |
8 #include <stddef.h> | 8 #include <stddef.h> |
9 | 9 |
10 #include <limits> | 10 #include <limits> |
11 #include <ostream> | 11 #include <ostream> |
12 #include <type_traits> | 12 #include <type_traits> |
13 | 13 |
14 #include "base/numerics/safe_conversions_impl.h" | 14 #include "base/numerics/safe_conversions_impl.h" |
15 | 15 |
16 namespace base { | 16 namespace base { |
17 | 17 |
18 // The following are helper constexpr template functions and classes for safely | |
19 // performing a range of conversions, assignments, and tests: | |
20 // | |
21 // checked_cast<> - Analogous to static_cast<> for numeric types, except | |
22 // that it CHECKs that the specified numeric conversion will not overflow | |
23 // or underflow. NaN source will always trigger a CHECK. | |
24 // The default CHECK triggers a crash, but the handler can be overriden. | |
25 // saturated_cast<> - Analogous to static_cast<> for numeric types, except | |
26 // that it returns a saturated result when the specified numeric conversion | |
27 // would otherwise overflow or underflow. An NaN source returns 0 by | |
28 // default, but can be overridden to return a different result. | |
29 // strict_cast<> - Analogous to static_cast<> for numeric types, except that | |
30 // it will cause a compile failure if the destination type is not large | |
31 // enough to contain any value in the source type. It performs no runtime | |
32 // checking and thus introduces no runtime overhead. | |
33 // IsValueInRangeForNumericType<>() - A convenience function that returns true | |
34 // if the type supplied to the template parameter can represent the value | |
35 // passed as an argument to the function. | |
36 // IsValueNegative<>() - A convenience function that will accept any arithmetic | |
37 // type as an argument and will return whether the value is less than zero. | |
38 // Unsigned types always return false. | |
39 // SafeUnsignedAbs() - Returns the absolute value of the supplied integer | |
40 // parameter as an unsigned result (thus avoiding an overflow if the value | |
41 // is the signed, two's complement minimum). | |
42 // StrictNumeric<> - A wrapper type that performs assignments and copies via | |
43 // the strict_cast<> template, and can perform valid arithmetic comparisons | |
44 // across any range of arithmetic types. StrictNumeric is the return type | |
45 // for values extracted from a CheckedNumeric class instance. The raw | |
46 // arithmetic value is extracted via static_cast to the underlying type. | |
47 // MakeStrictNum() - Creates a new StrictNumeric from the underlying type of | |
48 // the supplied arithmetic or StrictNumeric type. | |
49 | |
50 // Convenience function that returns true if the supplied value is in range | 18 // Convenience function that returns true if the supplied value is in range |
51 // for the destination type. | 19 // for the destination type. |
52 template <typename Dst, typename Src> | 20 template <typename Dst, typename Src> |
53 constexpr bool IsValueInRangeForNumericType(Src value) { | 21 constexpr bool IsValueInRangeForNumericType(Src value) { |
54 return internal::DstRangeRelationToSrcRange<Dst>(value).IsValid(); | 22 return internal::DstRangeRelationToSrcRange<Dst>(value).IsValid(); |
55 } | 23 } |
56 | 24 |
57 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. | 25 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. |
58 struct CheckOnFailure { | 26 struct CheckOnFailure { |
59 template <typename T> | 27 template <typename T> |
(...skipping 15 matching lines...) Expand all Loading... | |
75 typename Src> | 43 typename Src> |
76 constexpr Dst checked_cast(Src value) { | 44 constexpr Dst checked_cast(Src value) { |
77 // This throws a compile-time error on evaluating the constexpr if it can be | 45 // This throws a compile-time error on evaluating the constexpr if it can be |
78 // determined at compile-time as failing, otherwise it will CHECK at runtime. | 46 // determined at compile-time as failing, otherwise it will CHECK at runtime. |
79 using SrcType = typename internal::UnderlyingType<Src>::type; | 47 using SrcType = typename internal::UnderlyingType<Src>::type; |
80 return IsValueInRangeForNumericType<Dst, SrcType>(value) | 48 return IsValueInRangeForNumericType<Dst, SrcType>(value) |
81 ? static_cast<Dst>(static_cast<SrcType>(value)) | 49 ? static_cast<Dst>(static_cast<SrcType>(value)) |
82 : CheckHandler::template HandleFailure<Dst>(); | 50 : CheckHandler::template HandleFailure<Dst>(); |
83 } | 51 } |
84 | 52 |
53 // as_signed<> returns the supplied integral value (or integral castable | |
54 // Numeric template) cast as a signed integral of equivalent precision. | |
55 // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t) | |
56 template <typename Src> | |
57 constexpr typename std::make_signed< | |
58 typename base::internal::UnderlyingType<Src>::type>::type | |
59 as_signed(const Src value) { | |
60 static_assert(std::is_integral<decltype(as_signed(value))>::value, | |
61 "Argument must be a signed or unsigned integer type."); | |
62 return static_cast<decltype(as_signed(value))>(value); | |
63 } | |
64 | |
65 // as_unsigned<> returns the supplied integral value (or integral castable | |
66 // Numeric template) cast as an unsigned integral of equivalent precision. | |
67 // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t) | |
68 template <typename Src> | |
69 constexpr typename std::make_unsigned< | |
70 typename base::internal::UnderlyingType<Src>::type>::type | |
71 as_unsigned(const Src value) { | |
72 static_assert(std::is_integral<decltype(as_unsigned(value))>::value, | |
73 "Argument must be a signed or unsigned integer type."); | |
74 return static_cast<decltype(as_unsigned(value))>(value); | |
75 } | |
76 | |
85 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. | 77 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. |
86 template <typename T> | 78 template <typename T> |
87 struct SaturationDefaultHandler { | 79 struct SaturationDefaultLimits : public std::numeric_limits<T> { |
88 static constexpr T NaN() { | 80 static constexpr T NaN() { |
89 return std::numeric_limits<T>::has_quiet_NaN | 81 return std::numeric_limits<T>::has_quiet_NaN |
90 ? std::numeric_limits<T>::quiet_NaN() | 82 ? std::numeric_limits<T>::quiet_NaN() |
91 : T(); | 83 : T(); |
92 } | 84 } |
93 static constexpr T max() { return std::numeric_limits<T>::max(); } | 85 // static constexpr T required() is required by the library. |
dcheng
2017/06/28 07:25:06
required() => max()?
Also I don't quite understan
jschuh
2017/06/28 12:32:38
D'oh! Typo. But the gist is that you can provide a
| |
94 static constexpr T Overflow() { | 86 static constexpr T Overflow() { |
95 return std::numeric_limits<T>::has_infinity | 87 return std::numeric_limits<T>::has_infinity |
96 ? std::numeric_limits<T>::infinity() | 88 ? std::numeric_limits<T>::infinity() |
97 : std::numeric_limits<T>::max(); | 89 : std::numeric_limits<T>::max(); |
98 } | 90 } |
99 static constexpr T lowest() { return std::numeric_limits<T>::lowest(); } | 91 // static constexpr T lowest() is required by the library. |
100 static constexpr T Underflow() { | 92 static constexpr T Underflow() { |
101 return std::numeric_limits<T>::has_infinity | 93 return std::numeric_limits<T>::has_infinity |
102 ? std::numeric_limits<T>::infinity() * -1 | 94 ? std::numeric_limits<T>::infinity() * -1 |
103 : std::numeric_limits<T>::lowest(); | 95 : std::numeric_limits<T>::lowest(); |
104 } | 96 } |
105 }; | 97 }; |
106 | 98 |
107 namespace internal { | 99 namespace internal { |
108 | 100 |
109 template <typename Dst, template <typename> class S, typename Src> | 101 template <typename Dst, template <typename> class S, typename Src> |
110 constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { | 102 constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { |
111 // For some reason clang generates much better code when the branch is | 103 // For some reason clang generates much better code when the branch is |
112 // structured exactly this way, rather than a sequence of checks. | 104 // structured exactly this way, rather than a sequence of checks. |
113 return !constraint.IsOverflowFlagSet() | 105 return !constraint.IsOverflowFlagSet() |
114 ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value) | 106 ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value) |
115 : S<Dst>::Underflow()) | 107 : S<Dst>::Underflow()) |
116 // Skip this check for integral Src, which cannot be NaN. | 108 // Skip this check for integral Src, which cannot be NaN. |
117 : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet() | 109 : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet() |
118 ? S<Dst>::Overflow() | 110 ? S<Dst>::Overflow() |
119 : S<Dst>::NaN()); | 111 : S<Dst>::NaN()); |
120 } | 112 } |
121 | 113 |
122 // saturated_cast<> is analogous to static_cast<> for numeric types, except | 114 // saturated_cast<> is analogous to static_cast<> for numeric types, except |
123 // that the specified numeric conversion will saturate by default rather than | 115 // that the specified numeric conversion will saturate by default rather than |
124 // overflow or underflow, and NaN assignment to an integral will return 0. | 116 // overflow or underflow, and NaN assignment to an integral will return 0. |
125 // All boundary condition behaviors can be overriden with a custom handler. | 117 // All boundary condition behaviors can be overriden with a custom handler. |
126 template <typename Dst, | 118 template <typename Dst, |
127 template <typename> | 119 template <typename> class SaturationHandler = SaturationDefaultLimits, |
128 class SaturationHandler = SaturationDefaultHandler, | |
129 typename Src> | 120 typename Src> |
130 constexpr Dst saturated_cast(Src value) { | 121 constexpr Dst saturated_cast(Src value) { |
131 using SrcType = typename UnderlyingType<Src>::type; | 122 using SrcType = typename UnderlyingType<Src>::type; |
132 return saturated_cast_impl<Dst, SaturationHandler, SrcType>( | 123 return saturated_cast_impl<Dst, SaturationHandler, SrcType>( |
133 value, | 124 value, |
134 DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value)); | 125 DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value)); |
135 } | 126 } |
136 | 127 |
137 // strict_cast<> is analogous to static_cast<> for numeric types, except that | 128 // strict_cast<> is analogous to static_cast<> for numeric types, except that |
138 // it will cause a compile failure if the destination type is not large enough | 129 // it will cause a compile failure if the destination type is not large enough |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 return value; | 222 return value; |
232 } | 223 } |
233 | 224 |
234 // Overload the ostream output operator to make logging work nicely. | 225 // Overload the ostream output operator to make logging work nicely. |
235 template <typename T> | 226 template <typename T> |
236 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { | 227 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { |
237 os << static_cast<T>(value); | 228 os << static_cast<T>(value); |
238 return os; | 229 return os; |
239 } | 230 } |
240 | 231 |
241 #define STRICT_COMPARISON_OP(NAME, OP) \ | 232 #define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \ |
jschuh
2017/06/26 20:26:50
Generalized the implementation so ClampedNumeric c
| |
242 template <typename L, typename R, \ | 233 template <typename L, typename R, \ |
243 typename std::enable_if< \ | 234 typename std::enable_if< \ |
244 internal::IsStrictOp<L, R>::value>::type* = nullptr> \ | 235 internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \ |
245 constexpr bool operator OP(const L lhs, const R rhs) { \ | 236 constexpr bool operator OP(const L lhs, const R rhs) { \ |
246 return SafeCompare<NAME, typename UnderlyingType<L>::type, \ | 237 return SafeCompare<NAME, typename UnderlyingType<L>::type, \ |
247 typename UnderlyingType<R>::type>(lhs, rhs); \ | 238 typename UnderlyingType<R>::type>(lhs, rhs); \ |
248 } | 239 } |
249 | 240 |
250 STRICT_COMPARISON_OP(IsLess, <); | 241 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <); |
251 STRICT_COMPARISON_OP(IsLessOrEqual, <=); | 242 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=); |
252 STRICT_COMPARISON_OP(IsGreater, >); | 243 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >); |
253 STRICT_COMPARISON_OP(IsGreaterOrEqual, >=); | 244 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=); |
254 STRICT_COMPARISON_OP(IsEqual, ==); | 245 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==); |
255 STRICT_COMPARISON_OP(IsNotEqual, !=); | 246 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=); |
256 | 247 |
257 #undef STRICT_COMPARISON_OP | 248 }; // namespace internal |
258 }; | |
259 | 249 |
260 using internal::strict_cast; | 250 using internal::strict_cast; |
261 using internal::saturated_cast; | 251 using internal::saturated_cast; |
262 using internal::SafeUnsignedAbs; | 252 using internal::SafeUnsignedAbs; |
263 using internal::StrictNumeric; | 253 using internal::StrictNumeric; |
264 using internal::MakeStrictNum; | 254 using internal::MakeStrictNum; |
265 using internal::IsValueNegative; | 255 using internal::IsValueNegative; |
266 | 256 |
267 // Explicitly make a shorter size_t alias for convenience. | 257 // Explicitly make a shorter size_t alias for convenience. |
268 using SizeT = StrictNumeric<size_t>; | 258 using SizeT = StrictNumeric<size_t>; |
269 | 259 |
270 } // namespace base | 260 } // namespace base |
271 | 261 |
272 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 262 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ |
OLD | NEW |