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 // The default CHECK triggers a crash, but the handler can be overriden. | |
Tom Sepez
2016/11/30 23:58:27
Nit: can saturated_cast cause a CHECK?
jschuh
2016/12/01 00:58:55
Nope. Good catch.
| |
31 // strict_cast<> - Analogous to static_cast<> for numeric types, except that | |
32 // it will cause a compile failure if the destination type is not large | |
33 // enough to contain any value in the source type. It performs no runtime | |
34 // checking and thus introduces no runtime overhead. | |
35 // IsValueInRangeForNumericType<>() - A convenience function that returns true | |
36 // if the type supplied to the template parameter can represent in the | |
Tom Sepez
2016/11/30 23:58:27
nit: s/represent in/represent/
jschuh
2016/12/01 00:58:55
Done.
| |
37 // value passed as an argument to the function. | |
38 // IsValueNegative<>() - A convenience function that will accept any arithmetic | |
39 // type as an argument and will return whether the value is less than zero. | |
40 // Unsigned types always return false. | |
41 // StrictNumeric<> - A wrapper type that performs assignments and copies via | |
42 // the strict_cast<> template, and can perform valid arithmetic comparisons | |
43 // across any range of arithmetic types. StrictNumeric is the return type | |
44 // for values extracted from a CheckedNumeric class instance. The raw | |
45 // arithmetic value is extracted via static_cast to the underlying type. | |
46 | |
18 // Convenience function that returns true if the supplied value is in range | 47 // Convenience function that returns true if the supplied value is in range |
19 // for the destination type. | 48 // for the destination type. |
20 template <typename Dst, typename Src> | 49 template <typename Dst, typename Src> |
21 constexpr bool IsValueInRangeForNumericType(Src value) { | 50 constexpr bool IsValueInRangeForNumericType(Src value) { |
22 return internal::DstRangeRelationToSrcRange<Dst>(value) == | 51 return internal::DstRangeRelationToSrcRange<Dst>(value) == |
23 internal::RANGE_VALID; | 52 internal::RANGE_VALID; |
24 } | 53 } |
25 | 54 |
26 // Convenience function for determining if a numeric value is negative without | 55 // Convenience function for determining if a numeric value is negative without |
27 // throwing compiler warnings on: unsigned(value) < 0. | 56 // throwing compiler warnings on: unsigned(value) < 0. |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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) { |
104 return std::numeric_limits<Dst>::is_iec559 | 132 return std::numeric_limits<Dst>::is_iec559 |
105 ? static_cast<Dst>(value) // Floating point optimization. | 133 ? static_cast<Dst>(value) // Floating point optimization. |
106 : internal::saturated_cast_impl<Dst, NaNHandler>( | 134 : internal::saturated_cast_impl<Dst, NaNHandler>( |
107 value, internal::DstRangeRelationToSrcRange<Dst>(value)); | 135 value, internal::DstRangeRelationToSrcRange<Dst>(value)); |
108 } | 136 } |
109 | 137 |
110 // strict_cast<> is analogous to static_cast<> for numeric types, except that | 138 // 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 | 139 // 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. | 140 // to contain any value in the source type. It performs no runtime checking. |
113 template <typename Dst, typename Src> | 141 template <typename Dst, typename Src> |
114 constexpr Dst strict_cast(Src value) { | 142 constexpr Dst strict_cast(Src value) { |
115 static_assert(std::numeric_limits<Src>::is_specialized, | 143 static_assert(std::numeric_limits<Src>::is_specialized, |
116 "Argument must be numeric."); | 144 "Argument must be numeric."); |
117 static_assert(std::numeric_limits<Dst>::is_specialized, | 145 static_assert(std::numeric_limits<Dst>::is_specialized, |
118 "Result must be numeric."); | 146 "Result must be numeric."); |
119 static_assert((internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value == | 147 |
120 internal::NUMERIC_RANGE_CONTAINED), | 148 // If you got here from a compiler error, it's because you tried to assign |
121 "The numeric conversion is out of range for this type. You " | 149 // from a source type a destination type that has insufficient range. |
Tom Sepez
2016/11/30 23:58:27
nit: to a destination type.
jschuh
2016/12/01 00:58:55
Done.
| |
122 "should probably use one of the following conversion " | 150 // The solution may simply be to change the destination type you're assigning |
Tom Sepez
2016/11/30 23:58:27
nit: s/simply//
jschuh
2016/12/01 00:58:55
Done.
| |
123 "mechanisms on the value you want to pass:\n" | 151 // to, and use one large enough to represent the source. |
124 "- base::checked_cast\n" | 152 // If you're assigning from a CheckedNumeric<> class, you can use the |
125 "- base::saturated_cast\n" | 153 // AssignIfValid() member function, specify a narrower destination type to the |
126 "- base::CheckedNumeric"); | 154 // member value functions (e.g. val.template ValueOrDie<Dst>()), or use one of |
155 // the value helper functions (e.g. ValueOrDieForType<Dst>(val)). | |
156 // Alternatively, you may be better served with the checked_cast<> or | |
157 // saturated_cast<> template functions for your particular use case. | |
158 static_assert(internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value == | |
159 internal::NUMERIC_RANGE_CONTAINED, | |
160 "The source type is out of range for the destination type. " | |
161 "Please see strict_cast<> comments for more information."); | |
127 | 162 |
128 return static_cast<Dst>(value); | 163 return static_cast<Dst>(value); |
129 } | 164 } |
130 | 165 |
131 // StrictNumeric implements compile time range checking between numeric types by | 166 // StrictNumeric implements compile time range checking between numeric types by |
132 // wrapping assignment operations in a strict_cast. This class is intended to be | 167 // 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 | 168 // 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 | 169 // 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 | 170 // -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 | 171 // incrementally at API boundaries, making it easier to convert code so that it |
(...skipping 13 matching lines...) Expand all Loading... | |
150 constexpr StrictNumeric(const StrictNumeric<Src>& rhs) | 185 constexpr StrictNumeric(const StrictNumeric<Src>& rhs) |
151 : value_(strict_cast<T>(rhs.value_)) {} | 186 : value_(strict_cast<T>(rhs.value_)) {} |
152 | 187 |
153 // This is not an explicit constructor because we implicitly upgrade regular | 188 // This is not an explicit constructor because we implicitly upgrade regular |
154 // numerics to StrictNumerics to make them easier to use. | 189 // numerics to StrictNumerics to make them easier to use. |
155 template <typename Src> | 190 template <typename Src> |
156 constexpr StrictNumeric(Src value) | 191 constexpr StrictNumeric(Src value) |
157 : value_(strict_cast<T>(value)) {} | 192 : value_(strict_cast<T>(value)) {} |
158 | 193 |
159 // The numeric cast operator basically handles all the magic. | 194 // The numeric cast operator basically handles all the magic. |
160 template <typename Dst> | 195 template <typename Dst, |
196 typename std::enable_if< | |
197 ArithmeticOrUnderlyingEnum<Dst>::value>::type* = nullptr> | |
161 constexpr operator Dst() const { | 198 constexpr operator Dst() const { |
162 return strict_cast<Dst>(value_); | 199 return strict_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_); |
163 } | 200 } |
164 | 201 |
165 private: | 202 private: |
166 const T value_; | 203 const T value_; |
167 }; | 204 }; |
168 | 205 |
206 // Overload the ostream output operator to make logging work nicely. | |
207 template <typename T> | |
208 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { | |
209 os << static_cast<T>(value); | |
210 return os; | |
211 } | |
212 | |
213 #define STRICT_COMPARISON_OP(NAME, OP) \ | |
214 template <typename L, typename R, \ | |
215 typename std::enable_if< \ | |
216 internal::IsStrictOp<L, R>::value>::type* = nullptr> \ | |
217 constexpr bool operator OP(const L lhs, const R rhs) { \ | |
218 return SafeCompare<NAME, typename UnderlyingType<L>::type, \ | |
219 typename UnderlyingType<R>::type>(lhs, rhs); \ | |
220 } | |
221 | |
222 STRICT_COMPARISON_OP(IsLess, <); | |
223 STRICT_COMPARISON_OP(IsLessOrEqual, <=); | |
224 STRICT_COMPARISON_OP(IsGreater, >); | |
225 STRICT_COMPARISON_OP(IsGreaterOrEqual, >=); | |
226 STRICT_COMPARISON_OP(IsEqual, ==); | |
227 STRICT_COMPARISON_OP(IsNotEqual, !=); | |
228 | |
229 #undef STRICT_COMPARISON_OP | |
230 }; | |
231 | |
232 using internal::strict_cast; | |
233 using internal::saturated_cast; | |
234 using internal::StrictNumeric; | |
235 | |
169 // Explicitly make a shorter size_t typedef for convenience. | 236 // Explicitly make a shorter size_t typedef for convenience. |
170 typedef StrictNumeric<size_t> SizeT; | 237 typedef StrictNumeric<size_t> SizeT; |
171 | 238 |
172 } // namespace base | 239 } // namespace base |
173 | 240 |
174 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 241 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ |
OLD | NEW |