Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(139)

Side by Side Diff: base/numerics/safe_conversions.h

Issue 2578613002: Support saturation overrides in saturated_cast (Closed)
Patch Set: compile fix Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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>
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
48 // the supplied arithmetic or StrictNumeric type. 48 // the supplied arithmetic or StrictNumeric type.
49 49
50 // Convenience function that returns true if the supplied value is in range 50 // Convenience function that returns true if the supplied value is in range
51 // for the destination type. 51 // for the destination type.
52 template <typename Dst, typename Src> 52 template <typename Dst, typename Src>
53 constexpr bool IsValueInRangeForNumericType(Src value) { 53 constexpr bool IsValueInRangeForNumericType(Src value) {
54 return internal::DstRangeRelationToSrcRange<Dst>(value) == 54 return internal::DstRangeRelationToSrcRange<Dst>(value) ==
55 internal::RANGE_VALID; 55 internal::RANGE_VALID;
56 } 56 }
57 57
58 // Convenience function for determining if a numeric value is negative without
59 // throwing compiler warnings on: unsigned(value) < 0.
60 template <typename T,
61 typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
62 constexpr bool IsValueNegative(T value) {
63 static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
64 return value < 0;
65 }
66
67 template <typename T,
68 typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
69 constexpr bool IsValueNegative(T) {
70 static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
71 return false;
72 }
73
74 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. 58 // Forces a crash, like a CHECK(false). Used for numeric boundary errors.
75 struct CheckOnFailure { 59 struct CheckOnFailure {
76 template <typename T> 60 template <typename T>
77 static T HandleFailure() { 61 static T HandleFailure() {
78 #if defined(__GNUC__) || defined(__clang__) 62 #if defined(__GNUC__) || defined(__clang__)
79 __builtin_trap(); 63 __builtin_trap();
80 #else 64 #else
81 ((void)(*(volatile char*)0 = 0)); 65 ((void)(*(volatile char*)0 = 0));
82 #endif 66 #endif
83 return T(); 67 return T();
84 } 68 }
85 }; 69 };
86 70
87 // checked_cast<> is analogous to static_cast<> for numeric types, 71 // checked_cast<> is analogous to static_cast<> for numeric types,
88 // except that it CHECKs that the specified numeric conversion will not 72 // except that it CHECKs that the specified numeric conversion will not
89 // overflow or underflow. NaN source will always trigger a CHECK. 73 // overflow or underflow. NaN source will always trigger a CHECK.
90 template <typename Dst, 74 template <typename Dst,
91 class CheckHandler = CheckOnFailure, 75 class CheckHandler = CheckOnFailure,
92 typename Src> 76 typename Src>
93 constexpr Dst checked_cast(Src value) { 77 constexpr Dst checked_cast(Src value) {
94 // This throws a compile-time error on evaluating the constexpr if it can be 78 // This throws a compile-time error on evaluating the constexpr if it can be
95 // determined at compile-time as failing, otherwise it will CHECK at runtime. 79 // determined at compile-time as failing, otherwise it will CHECK at runtime.
96 using SrcType = typename internal::UnderlyingType<Src>::type; 80 using SrcType = typename internal::UnderlyingType<Src>::type;
97 return IsValueInRangeForNumericType<Dst, SrcType>(value) 81 return IsValueInRangeForNumericType<Dst, SrcType>(value)
98 ? static_cast<Dst>(static_cast<SrcType>(value)) 82 ? static_cast<Dst>(static_cast<SrcType>(value))
99 : CheckHandler::template HandleFailure<Dst>(); 83 : CheckHandler::template HandleFailure<Dst>();
100 } 84 }
101 85
102 // HandleNaN will return 0 in this case. 86 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
103 struct SaturatedCastNaNBehaviorReturnZero { 87 template <typename T>
104 template <typename T> 88 struct SaturatedCastDefaultHandler {
105 static constexpr T HandleFailure() { 89 static constexpr T HandleNaN() {
106 return T(); 90 return std::numeric_limits<T>::has_quiet_NaN
91 ? std::numeric_limits<T>::quiet_NaN()
92 : T();
93 }
94 static constexpr T max() { return std::numeric_limits<T>::max(); }
95 static constexpr T HandleOverflow() {
96 return std::numeric_limits<T>::has_infinity
97 ? std::numeric_limits<T>::infinity()
98 : std::numeric_limits<T>::max();
99 }
100 static constexpr T lowest() { return std::numeric_limits<T>::lowest(); }
101 static constexpr T HandleUnderflow() {
102 return std::numeric_limits<T>::has_infinity
103 ? std::numeric_limits<T>::infinity() * -1
104 : std::numeric_limits<T>::lowest();
107 } 105 }
108 }; 106 };
109 107
110 namespace internal { 108 namespace internal {
111 // These wrappers are used for C++11 constexpr support by avoiding both the 109 // saturated_cast<> is analogous to static_cast<> for numeric types, except
eae 2016/12/15 23:07:46 This is awesome! I'd love to replace the clampTo<T
jschuh 2016/12/16 00:31:00 Thanks, sgtm.
112 // declaration of local variables and invalid evaluation resulting from the 110 // that the specified numeric conversion will saturate by default rather than
113 // lack of "constexpr if" support in the saturated_cast template function. 111 // overflow or underflow, and NaN assignment to an integral will return 0.
114 // TODO(jschuh): Convert to single function with a switch once we support C++14. 112 // All boundary condition behaviors can be overriden with a custom handler.
115 template <
116 typename Dst,
117 class NaNHandler,
118 typename Src,
119 typename std::enable_if<std::is_integral<Dst>::value>::type* = nullptr>
120 constexpr Dst saturated_cast_impl(const Src value,
121 const RangeConstraint constraint) {
122 return constraint == RANGE_VALID
123 ? static_cast<Dst>(value)
124 : (constraint == RANGE_UNDERFLOW
125 ? std::numeric_limits<Dst>::lowest()
126 : (constraint == RANGE_OVERFLOW
127 ? std::numeric_limits<Dst>::max()
128 : NaNHandler::template HandleFailure<Dst>()));
129 }
130
131 template <typename Dst, 113 template <typename Dst,
132 class NaNHandler, 114 template <typename>
133 typename Src, 115 class SaturationHandler = SaturatedCastDefaultHandler,
134 typename std::enable_if<std::is_floating_point<Dst>::value>::type* =
135 nullptr>
136 constexpr Dst saturated_cast_impl(const Src value,
137 const RangeConstraint constraint) {
138 return constraint == RANGE_VALID
139 ? static_cast<Dst>(value)
140 : (constraint == RANGE_UNDERFLOW
141 ? -std::numeric_limits<Dst>::infinity()
142 : (constraint == RANGE_OVERFLOW
143 ? std::numeric_limits<Dst>::infinity()
144 : std::numeric_limits<Dst>::quiet_NaN()));
145 }
146
147 // saturated_cast<> is analogous to static_cast<> for numeric types, except
148 // that the specified numeric conversion will saturate rather than overflow or
149 // underflow. NaN assignment to an integral will defer the behavior to a
150 // specified class. By default, it will return 0.
151 template <typename Dst,
152 class NaNHandler = SaturatedCastNaNBehaviorReturnZero,
153 typename Src> 116 typename Src>
154 constexpr Dst saturated_cast(Src value) { 117 constexpr Dst saturated_cast(Src value) {
118 static_assert(
119 SaturationHandler<Dst>::lowest() < SaturationHandler<Dst>::max(), "");
155 using SrcType = typename UnderlyingType<Src>::type; 120 using SrcType = typename UnderlyingType<Src>::type;
156 return internal::saturated_cast_impl<Dst, NaNHandler>( 121 return IsGreaterOrEqual<SrcType, Dst>::Test(
eae 2016/12/15 23:07:46 Am I right in assuming that all these branches com
jschuh 2016/12/16 00:31:00 Yes. All of the additional craziness is to cover a
157 value, internal::DstRangeRelationToSrcRange<Dst, SrcType>(value)); 122 value, NarrowingRange<Dst, SrcType, SaturationHandler>::lowest())
123 ? (IsLessOrEqual<SrcType, Dst>::Test(
124 value,
125 NarrowingRange<Dst, SrcType, SaturationHandler>::max())
126 ? static_cast<Dst>(value)
127 : SaturationHandler<Dst>::HandleOverflow())
128 : (std::is_integral<SrcType>::value ||
129 std::is_floating_point<Dst>::value ||
130 IsLessOrEqual<SrcType, Dst>::Test(
131 value, NarrowingRange<Dst, SrcType,
132 SaturationHandler>::max())
133 ? SaturationHandler<Dst>::HandleUnderflow()
134 : SaturationHandler<Dst>::HandleNaN());
158 } 135 }
159 136
160 // strict_cast<> is analogous to static_cast<> for numeric types, except that 137 // strict_cast<> is analogous to static_cast<> for numeric types, except that
161 // it will cause a compile failure if the destination type is not large enough 138 // it will cause a compile failure if the destination type is not large enough
162 // to contain any value in the source type. It performs no runtime checking. 139 // to contain any value in the source type. It performs no runtime checking.
163 template <typename Dst, typename Src> 140 template <typename Dst, typename Src>
164 constexpr Dst strict_cast(Src value) { 141 constexpr Dst strict_cast(Src value) {
165 using SrcType = typename UnderlyingType<Src>::type; 142 using SrcType = typename UnderlyingType<Src>::type;
166 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); 143 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
167 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); 144 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 STRICT_COMPARISON_OP(IsNotEqual, !=); 255 STRICT_COMPARISON_OP(IsNotEqual, !=);
279 256
280 #undef STRICT_COMPARISON_OP 257 #undef STRICT_COMPARISON_OP
281 }; 258 };
282 259
283 using internal::strict_cast; 260 using internal::strict_cast;
284 using internal::saturated_cast; 261 using internal::saturated_cast;
285 using internal::SafeUnsignedAbs; 262 using internal::SafeUnsignedAbs;
286 using internal::StrictNumeric; 263 using internal::StrictNumeric;
287 using internal::MakeStrictNum; 264 using internal::MakeStrictNum;
265 using internal::IsValueNegative;
288 266
289 // Explicitly make a shorter size_t alias for convenience. 267 // Explicitly make a shorter size_t alias for convenience.
290 using SizeT = StrictNumeric<size_t>; 268 using SizeT = StrictNumeric<size_t>;
291 269
292 } // namespace base 270 } // namespace base
293 271
294 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ 272 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_
OLDNEW
« no previous file with comments | « no previous file | base/numerics/safe_conversions_impl.h » ('j') | base/numerics/safe_conversions_impl.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698