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

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

Issue 2945433003: Add ClampedNumeric templates (Closed)
Patch Set: final Created 3 years, 5 months 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
« no previous file with comments | « base/numerics/clamped_math_impl.h ('k') | base/numerics/safe_conversions_impl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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>
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
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.
78 // You may provide your own limits (e.g. to saturated_cast) so long as you
79 // implement all of the static constexpr member functions in the class below.
86 template <typename T> 80 template <typename T>
87 struct SaturationDefaultHandler { 81 struct SaturationDefaultLimits : public std::numeric_limits<T> {
88 static constexpr T NaN() { 82 static constexpr T NaN() {
89 return std::numeric_limits<T>::has_quiet_NaN 83 return std::numeric_limits<T>::has_quiet_NaN
90 ? std::numeric_limits<T>::quiet_NaN() 84 ? std::numeric_limits<T>::quiet_NaN()
91 : T(); 85 : T();
92 } 86 }
93 static constexpr T max() { return std::numeric_limits<T>::max(); } 87 using std::numeric_limits<T>::max;
94 static constexpr T Overflow() { 88 static constexpr T Overflow() {
95 return std::numeric_limits<T>::has_infinity 89 return std::numeric_limits<T>::has_infinity
96 ? std::numeric_limits<T>::infinity() 90 ? std::numeric_limits<T>::infinity()
97 : std::numeric_limits<T>::max(); 91 : std::numeric_limits<T>::max();
98 } 92 }
99 static constexpr T lowest() { return std::numeric_limits<T>::lowest(); } 93 using std::numeric_limits<T>::lowest;
100 static constexpr T Underflow() { 94 static constexpr T Underflow() {
101 return std::numeric_limits<T>::has_infinity 95 return std::numeric_limits<T>::has_infinity
102 ? std::numeric_limits<T>::infinity() * -1 96 ? std::numeric_limits<T>::infinity() * -1
103 : std::numeric_limits<T>::lowest(); 97 : std::numeric_limits<T>::lowest();
104 } 98 }
105 }; 99 };
106 100
107 namespace internal { 101 namespace internal {
108 102
109 template <typename Dst, template <typename> class S, typename Src> 103 template <typename Dst, template <typename> class S, typename Src>
110 constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { 104 constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
111 // For some reason clang generates much better code when the branch is 105 // For some reason clang generates much better code when the branch is
112 // structured exactly this way, rather than a sequence of checks. 106 // structured exactly this way, rather than a sequence of checks.
113 return !constraint.IsOverflowFlagSet() 107 return !constraint.IsOverflowFlagSet()
114 ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value) 108 ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
115 : S<Dst>::Underflow()) 109 : S<Dst>::Underflow())
116 // Skip this check for integral Src, which cannot be NaN. 110 // Skip this check for integral Src, which cannot be NaN.
117 : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet() 111 : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet()
118 ? S<Dst>::Overflow() 112 ? S<Dst>::Overflow()
119 : S<Dst>::NaN()); 113 : S<Dst>::NaN());
120 } 114 }
121 115
122 // saturated_cast<> is analogous to static_cast<> for numeric types, except 116 // saturated_cast<> is analogous to static_cast<> for numeric types, except
123 // that the specified numeric conversion will saturate by default rather than 117 // that the specified numeric conversion will saturate by default rather than
124 // overflow or underflow, and NaN assignment to an integral will return 0. 118 // overflow or underflow, and NaN assignment to an integral will return 0.
125 // All boundary condition behaviors can be overriden with a custom handler. 119 // All boundary condition behaviors can be overriden with a custom handler.
126 template <typename Dst, 120 template <typename Dst,
127 template <typename> 121 template <typename> class SaturationHandler = SaturationDefaultLimits,
128 class SaturationHandler = SaturationDefaultHandler,
129 typename Src> 122 typename Src>
130 constexpr Dst saturated_cast(Src value) { 123 constexpr Dst saturated_cast(Src value) {
131 using SrcType = typename UnderlyingType<Src>::type; 124 using SrcType = typename UnderlyingType<Src>::type;
132 return saturated_cast_impl<Dst, SaturationHandler, SrcType>( 125 return saturated_cast_impl<Dst, SaturationHandler, SrcType>(
133 value, 126 value,
134 DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value)); 127 DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value));
135 } 128 }
136 129
137 // strict_cast<> is analogous to static_cast<> for numeric types, except that 130 // 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 131 // 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
231 return value; 224 return value;
232 } 225 }
233 226
234 // Overload the ostream output operator to make logging work nicely. 227 // Overload the ostream output operator to make logging work nicely.
235 template <typename T> 228 template <typename T>
236 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { 229 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
237 os << static_cast<T>(value); 230 os << static_cast<T>(value);
238 return os; 231 return os;
239 } 232 }
240 233
241 #define STRICT_COMPARISON_OP(NAME, OP) \ 234 #define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
242 template <typename L, typename R, \ 235 template <typename L, typename R, \
243 typename std::enable_if< \ 236 typename std::enable_if< \
244 internal::IsStrictOp<L, R>::value>::type* = nullptr> \ 237 internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
245 constexpr bool operator OP(const L lhs, const R rhs) { \ 238 constexpr bool operator OP(const L lhs, const R rhs) { \
246 return SafeCompare<NAME, typename UnderlyingType<L>::type, \ 239 return SafeCompare<NAME, typename UnderlyingType<L>::type, \
247 typename UnderlyingType<R>::type>(lhs, rhs); \ 240 typename UnderlyingType<R>::type>(lhs, rhs); \
248 } 241 }
249 242
250 STRICT_COMPARISON_OP(IsLess, <); 243 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <);
251 STRICT_COMPARISON_OP(IsLessOrEqual, <=); 244 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=);
252 STRICT_COMPARISON_OP(IsGreater, >); 245 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >);
253 STRICT_COMPARISON_OP(IsGreaterOrEqual, >=); 246 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=);
254 STRICT_COMPARISON_OP(IsEqual, ==); 247 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==);
255 STRICT_COMPARISON_OP(IsNotEqual, !=); 248 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=);
256 249
257 #undef STRICT_COMPARISON_OP 250 }; // namespace internal
258 };
259 251
260 using internal::strict_cast; 252 using internal::strict_cast;
261 using internal::saturated_cast; 253 using internal::saturated_cast;
262 using internal::SafeUnsignedAbs; 254 using internal::SafeUnsignedAbs;
263 using internal::StrictNumeric; 255 using internal::StrictNumeric;
264 using internal::MakeStrictNum; 256 using internal::MakeStrictNum;
265 using internal::IsValueNegative; 257 using internal::IsValueNegative;
266 258
267 // Explicitly make a shorter size_t alias for convenience. 259 // Explicitly make a shorter size_t alias for convenience.
268 using SizeT = StrictNumeric<size_t>; 260 using SizeT = StrictNumeric<size_t>;
269 261
270 } // namespace base 262 } // namespace base
271 263
272 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ 264 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_
OLDNEW
« no previous file with comments | « base/numerics/clamped_math_impl.h ('k') | base/numerics/safe_conversions_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698