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 <cassert> | 10 #include <cassert> |
11 #include <limits> | 11 #include <limits> |
| 12 #include <ostream> |
12 #include <type_traits> | 13 #include <type_traits> |
13 | 14 |
14 #include "base/numerics/safe_conversions_impl.h" | 15 #include "base/numerics/safe_conversions_impl.h" |
15 | 16 |
16 namespace base { | 17 namespace base { |
17 | 18 |
| 19 // The following are helper constexpr template functions and classes for safely |
| 20 // performing a range of conversions, assignments, and tests: |
| 21 // |
| 22 // checked_cast<> - Analogous to static_cast<> for numeric types, except |
| 23 // that it CHECKs that the specified numeric conversion will not overflow |
| 24 // or underflow. NaN source will always trigger a CHECK. |
| 25 // The default CHECK triggers a crash, but the handler can be overriden. |
| 26 // saturated_cast<> - Analogous to static_cast<> for numeric types, except |
| 27 // that it returns a saturated result when the specified numeric conversion |
| 28 // would otherwise overflow or underflow. An NaN source returns 0 by |
| 29 // default, but can be overridden to return a different result. |
| 30 // strict_cast<> - Analogous to static_cast<> for numeric types, except that |
| 31 // it will cause a compile failure if the destination type is not large |
| 32 // enough to contain any value in the source type. It performs no runtime |
| 33 // checking and thus introduces no runtime overhead. |
| 34 // IsValueInRangeForNumericType<>() - A convenience function that returns true |
| 35 // if the type supplied to the template parameter can represent the value |
| 36 // passed as an argument to the function. |
| 37 // IsValueNegative<>() - A convenience function that will accept any arithmetic |
| 38 // type as an argument and will return whether the value is less than zero. |
| 39 // Unsigned types always return false. |
| 40 // StrictNumeric<> - A wrapper type that performs assignments and copies via |
| 41 // the strict_cast<> template, and can perform valid arithmetic comparisons |
| 42 // across any range of arithmetic types. StrictNumeric is the return type |
| 43 // for values extracted from a CheckedNumeric class instance. The raw |
| 44 // arithmetic value is extracted via static_cast to the underlying type. |
| 45 |
18 // Convenience function that returns true if the supplied value is in range | 46 // Convenience function that returns true if the supplied value is in range |
19 // for the destination type. | 47 // for the destination type. |
20 template <typename Dst, typename Src> | 48 template <typename Dst, typename Src> |
21 constexpr bool IsValueInRangeForNumericType(Src value) { | 49 constexpr bool IsValueInRangeForNumericType(Src value) { |
22 return internal::DstRangeRelationToSrcRange<Dst>(value) == | 50 return internal::DstRangeRelationToSrcRange<Dst>(value) == |
23 internal::RANGE_VALID; | 51 internal::RANGE_VALID; |
24 } | 52 } |
25 | 53 |
26 // Convenience function for determining if a numeric value is negative without | 54 // Convenience function for determining if a numeric value is negative without |
27 // throwing compiler warnings on: unsigned(value) < 0. | 55 // throwing compiler warnings on: unsigned(value) < 0. |
(...skipping 28 matching lines...) Expand all Loading... |
56 | 84 |
57 // checked_cast<> is analogous to static_cast<> for numeric types, | 85 // checked_cast<> is analogous to static_cast<> for numeric types, |
58 // except that it CHECKs that the specified numeric conversion will not | 86 // except that it CHECKs that the specified numeric conversion will not |
59 // overflow or underflow. NaN source will always trigger a CHECK. | 87 // overflow or underflow. NaN source will always trigger a CHECK. |
60 template <typename Dst, | 88 template <typename Dst, |
61 class CheckHandler = CheckOnFailure, | 89 class CheckHandler = CheckOnFailure, |
62 typename Src> | 90 typename Src> |
63 constexpr Dst checked_cast(Src value) { | 91 constexpr Dst checked_cast(Src value) { |
64 // This throws a compile-time error on evaluating the constexpr if it can be | 92 // This throws a compile-time error on evaluating the constexpr if it can be |
65 // determined at compile-time as failing, otherwise it will CHECK at runtime. | 93 // determined at compile-time as failing, otherwise it will CHECK at runtime. |
66 return IsValueInRangeForNumericType<Dst>(value) | 94 using SrcType = typename internal::UnderlyingType<Src>::type; |
67 ? static_cast<Dst>(value) | 95 return IsValueInRangeForNumericType<Dst, SrcType>(value) |
| 96 ? static_cast<Dst>(static_cast<SrcType>(value)) |
68 : CheckHandler::template HandleFailure<Dst>(); | 97 : CheckHandler::template HandleFailure<Dst>(); |
69 } | 98 } |
70 | 99 |
71 // HandleNaN will return 0 in this case. | 100 // HandleNaN will return 0 in this case. |
72 struct SaturatedCastNaNBehaviorReturnZero { | 101 struct SaturatedCastNaNBehaviorReturnZero { |
73 template <typename T> | 102 template <typename T> |
74 static constexpr T HandleFailure() { | 103 static constexpr T HandleFailure() { |
75 return T(); | 104 return T(); |
76 } | 105 } |
77 }; | 106 }; |
78 | 107 |
79 namespace internal { | 108 namespace internal { |
80 // This wrapper is used for C++11 constexpr support by avoiding the declaration | 109 // This wrapper is used for C++11 constexpr support by avoiding the declaration |
81 // of local variables in the saturated_cast template function. | 110 // of local variables in the saturated_cast template function. |
82 // TODO(jschuh): convert this back to a switch once we support C++14. | 111 // TODO(jschuh): convert this back to a switch once we support C++14. |
83 template <typename Dst, class NaNHandler, typename Src> | 112 template <typename Dst, class NaNHandler, typename Src> |
84 constexpr Dst saturated_cast_impl(const Src value, | 113 constexpr Dst saturated_cast_impl(const Src value, |
85 const RangeConstraint constraint) { | 114 const RangeConstraint constraint) { |
86 return constraint == RANGE_VALID | 115 return constraint == RANGE_VALID |
87 ? static_cast<Dst>(value) | 116 ? static_cast<Dst>(value) |
88 : (constraint == RANGE_UNDERFLOW | 117 : (constraint == RANGE_UNDERFLOW |
89 ? std::numeric_limits<Dst>::min() | 118 ? std::numeric_limits<Dst>::min() |
90 : (constraint == RANGE_OVERFLOW | 119 : (constraint == RANGE_OVERFLOW |
91 ? std::numeric_limits<Dst>::max() | 120 ? std::numeric_limits<Dst>::max() |
92 : NaNHandler::template HandleFailure<Dst>())); | 121 : NaNHandler::template HandleFailure<Dst>())); |
93 } | 122 } |
94 } // namespace internal | |
95 | 123 |
96 // saturated_cast<> is analogous to static_cast<> for numeric types, except | 124 // saturated_cast<> is analogous to static_cast<> for numeric types, except |
97 // that the specified numeric conversion will saturate rather than overflow or | 125 // that the specified numeric conversion will saturate rather than overflow or |
98 // underflow. NaN assignment to an integral will defer the behavior to a | 126 // underflow. NaN assignment to an integral will defer the behavior to a |
99 // specified class. By default, it will return 0. | 127 // specified class. By default, it will return 0. |
100 template <typename Dst, | 128 template <typename Dst, |
101 class NaNHandler = SaturatedCastNaNBehaviorReturnZero, | 129 class NaNHandler = SaturatedCastNaNBehaviorReturnZero, |
102 typename Src> | 130 typename Src> |
103 constexpr Dst saturated_cast(Src value) { | 131 constexpr Dst saturated_cast(Src value) { |
| 132 using SrcType = typename UnderlyingType<Src>::type; |
104 return std::numeric_limits<Dst>::is_iec559 | 133 return std::numeric_limits<Dst>::is_iec559 |
105 ? static_cast<Dst>(value) // Floating point optimization. | 134 ? static_cast<Dst>( |
| 135 static_cast<SrcType>(value)) // Floating point optimization. |
106 : internal::saturated_cast_impl<Dst, NaNHandler>( | 136 : internal::saturated_cast_impl<Dst, NaNHandler>( |
107 value, internal::DstRangeRelationToSrcRange<Dst>(value)); | 137 value, |
| 138 internal::DstRangeRelationToSrcRange<Dst, SrcType>(value)); |
108 } | 139 } |
109 | 140 |
110 // strict_cast<> is analogous to static_cast<> for numeric types, except that | 141 // strict_cast<> is analogous to static_cast<> for numeric types, except that |
111 // it will cause a compile failure if the destination type is not large enough | 142 // it will cause a compile failure if the destination type is not large enough |
112 // to contain any value in the source type. It performs no runtime checking. | 143 // to contain any value in the source type. It performs no runtime checking. |
113 template <typename Dst, typename Src> | 144 template <typename Dst, typename Src> |
114 constexpr Dst strict_cast(Src value) { | 145 constexpr Dst strict_cast(Src value) { |
115 static_assert(std::numeric_limits<Src>::is_specialized, | 146 using SrcType = typename UnderlyingType<Src>::type; |
116 "Argument must be numeric."); | 147 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); |
117 static_assert(std::numeric_limits<Dst>::is_specialized, | 148 static_assert(std::numeric_limits<Dst>::is_specialized, |
118 "Result must be numeric."); | 149 "Result must be numeric."); |
119 static_assert((internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value == | |
120 internal::NUMERIC_RANGE_CONTAINED), | |
121 "The numeric conversion is out of range for this type. You " | |
122 "should probably use one of the following conversion " | |
123 "mechanisms on the value you want to pass:\n" | |
124 "- base::checked_cast\n" | |
125 "- base::saturated_cast\n" | |
126 "- base::CheckedNumeric"); | |
127 | 150 |
128 return static_cast<Dst>(value); | 151 // If you got here from a compiler error, it's because you tried to assign |
| 152 // from a source type to a destination type that has insufficient range. |
| 153 // The solution may be to change the destination type you're assigning to, |
| 154 // and use one large enough to represent the source. |
| 155 // Alternatively, you may be better served with the checked_cast<> or |
| 156 // saturated_cast<> template functions for your particular use case. |
| 157 static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value == |
| 158 NUMERIC_RANGE_CONTAINED, |
| 159 "The source type is out of range for the destination type. " |
| 160 "Please see strict_cast<> comments for more information."); |
| 161 |
| 162 return static_cast<Dst>(static_cast<SrcType>(value)); |
129 } | 163 } |
130 | 164 |
| 165 // Some wrappers to statically check that a type is in range. |
| 166 template <typename Dst, typename Src, class Enable = void> |
| 167 struct IsNumericRangeContained { |
| 168 static const bool value = false; |
| 169 }; |
| 170 |
| 171 template <typename Dst, typename Src> |
| 172 struct IsNumericRangeContained< |
| 173 Dst, |
| 174 Src, |
| 175 typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value && |
| 176 ArithmeticOrUnderlyingEnum<Src>::value>::type> { |
| 177 static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value == |
| 178 NUMERIC_RANGE_CONTAINED; |
| 179 }; |
| 180 |
131 // StrictNumeric implements compile time range checking between numeric types by | 181 // StrictNumeric implements compile time range checking between numeric types by |
132 // wrapping assignment operations in a strict_cast. This class is intended to be | 182 // wrapping assignment operations in a strict_cast. This class is intended to be |
133 // used for function arguments and return types, to ensure the destination type | 183 // used for function arguments and return types, to ensure the destination type |
134 // can always contain the source type. This is essentially the same as enforcing | 184 // can always contain the source type. This is essentially the same as enforcing |
135 // -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied | 185 // -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied |
136 // incrementally at API boundaries, making it easier to convert code so that it | 186 // incrementally at API boundaries, making it easier to convert code so that it |
137 // compiles cleanly with truncation warnings enabled. | 187 // compiles cleanly with truncation warnings enabled. |
138 // This template should introduce no runtime overhead, but it also provides no | 188 // This template should introduce no runtime overhead, but it also provides no |
139 // runtime checking of any of the associated mathematical operations. Use | 189 // runtime checking of any of the associated mathematical operations. Use |
140 // CheckedNumeric for runtime range checks of the actual value being assigned. | 190 // CheckedNumeric for runtime range checks of the actual value being assigned. |
141 template <typename T> | 191 template <typename T> |
142 class StrictNumeric { | 192 class StrictNumeric { |
143 public: | 193 public: |
144 typedef T type; | 194 typedef T type; |
145 | 195 |
146 constexpr StrictNumeric() : value_(0) {} | 196 constexpr StrictNumeric() : value_(0) {} |
147 | 197 |
148 // Copy constructor. | 198 // Copy constructor. |
149 template <typename Src> | 199 template <typename Src> |
150 constexpr StrictNumeric(const StrictNumeric<Src>& rhs) | 200 constexpr StrictNumeric(const StrictNumeric<Src>& rhs) |
151 : value_(strict_cast<T>(rhs.value_)) {} | 201 : value_(strict_cast<T>(rhs.value_)) {} |
152 | 202 |
153 // This is not an explicit constructor because we implicitly upgrade regular | 203 // This is not an explicit constructor because we implicitly upgrade regular |
154 // numerics to StrictNumerics to make them easier to use. | 204 // numerics to StrictNumerics to make them easier to use. |
155 template <typename Src> | 205 template <typename Src> |
156 constexpr StrictNumeric(Src value) | 206 constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit) |
157 : value_(strict_cast<T>(value)) {} | 207 : value_(strict_cast<T>(value)) {} |
158 | 208 |
159 // The numeric cast operator basically handles all the magic. | 209 // If you got here from a compiler error, it's because you tried to assign |
160 template <typename Dst> | 210 // from a source type to a destination type that has insufficient range. |
| 211 // The solution may be to change the destination type you're assigning to, |
| 212 // and use one large enough to represent the source. |
| 213 // If you're assigning from a CheckedNumeric<> class, you may be able to use |
| 214 // the AssignIfValid() member function, specify a narrower destination type to |
| 215 // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one |
| 216 // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)). |
| 217 // If you've encountered an _ambiguous overload_ you can use a static_cast<> |
| 218 // to explicitly cast the result to the destination type. |
| 219 // If none of that works, you may be better served with the checked_cast<> or |
| 220 // saturated_cast<> template functions for your particular use case. |
| 221 template <typename Dst, |
| 222 typename std::enable_if< |
| 223 IsNumericRangeContained<Dst, T>::value>::type* = nullptr> |
161 constexpr operator Dst() const { | 224 constexpr operator Dst() const { |
162 return strict_cast<Dst>(value_); | 225 return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_); |
163 } | 226 } |
164 | 227 |
165 private: | 228 private: |
166 const T value_; | 229 const T value_; |
167 }; | 230 }; |
168 | 231 |
| 232 // Overload the ostream output operator to make logging work nicely. |
| 233 template <typename T> |
| 234 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { |
| 235 os << static_cast<T>(value); |
| 236 return os; |
| 237 } |
| 238 |
| 239 #define STRICT_COMPARISON_OP(NAME, OP) \ |
| 240 template <typename L, typename R, \ |
| 241 typename std::enable_if< \ |
| 242 internal::IsStrictOp<L, R>::value>::type* = nullptr> \ |
| 243 constexpr bool operator OP(const L lhs, const R rhs) { \ |
| 244 return SafeCompare<NAME, typename UnderlyingType<L>::type, \ |
| 245 typename UnderlyingType<R>::type>(lhs, rhs); \ |
| 246 } |
| 247 |
| 248 STRICT_COMPARISON_OP(IsLess, <); |
| 249 STRICT_COMPARISON_OP(IsLessOrEqual, <=); |
| 250 STRICT_COMPARISON_OP(IsGreater, >); |
| 251 STRICT_COMPARISON_OP(IsGreaterOrEqual, >=); |
| 252 STRICT_COMPARISON_OP(IsEqual, ==); |
| 253 STRICT_COMPARISON_OP(IsNotEqual, !=); |
| 254 |
| 255 #undef STRICT_COMPARISON_OP |
| 256 }; |
| 257 |
| 258 using internal::strict_cast; |
| 259 using internal::saturated_cast; |
| 260 using internal::StrictNumeric; |
| 261 |
169 // Explicitly make a shorter size_t typedef for convenience. | 262 // Explicitly make a shorter size_t typedef for convenience. |
170 typedef StrictNumeric<size_t> SizeT; | 263 typedef StrictNumeric<size_t> SizeT; |
171 | 264 |
172 } // namespace base | 265 } // namespace base |
173 | 266 |
174 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 267 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ |
OLD | NEW |