OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2017 The WebRTC Project Authors. All rights reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 // Minimum and maximum | |
12 // =================== | |
13 // | |
14 // rtc::SafeMin(x, y) | |
15 // rtc::SafeMax(x, y) | |
16 // | |
17 // Accept two arguments of either any two integral or any two floating-point | |
18 // types, and return the smaller and larger value, respectively, with no | |
19 // truncation or wrap-around. If only one of the input types is statically | |
20 // guaranteed to be able to represent the result, the return type is that type; | |
21 // if either one would do, the result type is the smaller type. (One of these | |
22 // two cases always applies.) | |
23 // | |
24 // (The case with one floating-point and one integral type is not allowed, | |
25 // because the floating-point type will have greater range, but may not have | |
26 // sufficient precision to represent the integer value exactly.) | |
27 // | |
28 // Requesting a specific return type | |
29 // ================================= | |
30 // | |
31 // Both functions allow callers to explicitly specify the return type as a | |
32 // template parameter, overriding the default return type. E.g. | |
33 // | |
34 // rtc::SafeMin<int>(x, y) // returns an int | |
35 // | |
36 // If the requested type is statically guaranteed to be able to represent the | |
37 // result, then everything's fine, and the return type is as requested. But if | |
38 // the requested type is too small, a static_assert is triggered. | |
39 | |
40 #ifndef WEBRTC_BASE_SAFE_MINMAX_H_ | |
41 #define WEBRTC_BASE_SAFE_MINMAX_H_ | |
42 | |
43 #include <limits> | |
44 #include <type_traits> | |
45 | |
46 #include "webrtc/base/checks.h" | |
47 #include "webrtc/base/safe_compare.h" | |
48 #include "webrtc/base/type_traits.h" | |
49 | |
50 namespace rtc { | |
51 | |
52 namespace safe_minmax_impl { | |
53 | |
54 // Make the range of a type available via something other than a constexpr | |
55 // function, to work around MSVC limitations. See | |
56 // https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expres sion-sfinae-in-vs-2015-update-1/ | |
57 template <typename T> | |
58 struct Limits { | |
59 static constexpr T lowest = std::numeric_limits<T>::lowest(); | |
60 static constexpr T max = std::numeric_limits<T>::max(); | |
61 }; | |
62 | |
63 template <typename T, bool is_enum = std::is_enum<T>::value> | |
64 struct UnderlyingType; | |
65 | |
66 template <typename T> | |
67 struct UnderlyingType<T, false> { | |
68 using type = T; | |
69 }; | |
70 | |
71 template <typename T> | |
72 struct UnderlyingType<T, true> { | |
73 using type = typename std::underlying_type<T>::type; | |
74 }; | |
75 | |
76 // Given two types T1 and T2, find types that can hold the smallest (in | |
77 // ::min_t) and the largest (in ::max_t) of the two values. | |
78 template <typename T1, | |
79 typename T2, | |
80 bool all_int = IsIntlike<T1>::value&& IsIntlike<T2>::value> | |
81 struct MType; | |
82 | |
83 // Specialization for when at least one of the types is floating-point. | |
84 template <typename T1, typename T2> | |
85 struct MType<T1, T2, false> { | |
86 using min_t = typename std::common_type<T1, T2>::type; | |
87 static_assert(std::is_same<min_t, T1>::value || | |
88 std::is_same<min_t, T2>::value, | |
89 ""); | |
90 | |
91 using max_t = typename std::common_type<T1, T2>::type; | |
92 static_assert(std::is_same<max_t, T1>::value || | |
93 std::is_same<max_t, T2>::value, | |
94 ""); | |
95 }; | |
96 | |
97 // Specialization for when both types are integral. | |
98 template <typename T1, typename T2> | |
99 struct MType<T1, T2, true> { | |
100 // The type with the lowest minimum value. In case of a tie, the type with | |
101 // the lowest maximum value. In case that too is a tie, the types have the | |
102 // same range, and we arbitrarily pick T1. | |
103 using min_t = typename std::conditional< | |
104 safe_cmp::Lt(Limits<T1>::lowest, Limits<T2>::lowest), | |
105 T1, | |
106 typename std::conditional< | |
107 safe_cmp::Gt(Limits<T1>::lowest, Limits<T2>::lowest), | |
108 T2, | |
109 typename std::conditional<safe_cmp::Le(Limits<T1>::max, | |
110 Limits<T2>::max), | |
111 T1, | |
112 T2>::type>::type>::type; | |
113 static_assert(std::is_same<min_t, T1>::value || | |
114 std::is_same<min_t, T2>::value, | |
115 ""); | |
116 | |
117 // The type with the highest maximum value. In case of a tie, the types have | |
118 // the same range (because in C++, integer types with the same maximum also | |
119 // have the same minimum). | |
120 static_assert(safe_cmp::Ne(Limits<T1>::max, Limits<T2>::max) || | |
121 safe_cmp::Eq(Limits<T1>::lowest, Limits<T2>::lowest), | |
122 "integer types with the same max should have the same min"); | |
123 using max_t = typename std:: | |
124 conditional<safe_cmp::Ge(Limits<T1>::max, Limits<T2>::max), T1, T2>::type; | |
125 static_assert(std::is_same<max_t, T1>::value || | |
126 std::is_same<max_t, T2>::value, | |
127 ""); | |
128 }; | |
129 | |
130 // A dummy type that we pass around at compile time but never actually use. | |
131 // Declared but not defined. | |
132 struct DefaultType; | |
133 | |
134 // ::type is A, except we fall back to B if A is DefaultType. We static_assert | |
135 // that the chosen type can hold all values that B can hold. | |
136 template <typename A, typename B> | |
137 struct TypeOr { | |
138 using type = typename std:: | |
139 conditional<std::is_same<A, DefaultType>::value, B, A>::type; | |
140 static_assert(safe_cmp::Le(Limits<type>::lowest, Limits<B>::lowest) && | |
141 safe_cmp::Ge(Limits<type>::max, Limits<B>::max), | |
142 "The specified type isn't large enough"); | |
143 static_assert(IsIntlike<type>::value == IsIntlike<B>::value && | |
144 std::is_floating_point<type>::value == | |
145 std::is_floating_point<type>::value, | |
146 "float<->int conversions not allowed"); | |
147 }; | |
148 | |
149 } // namespace safe_minmax_impl | |
150 | |
151 template <typename R = safe_minmax_impl::DefaultType, | |
152 typename T1 = safe_minmax_impl::DefaultType, | |
153 typename T2 = safe_minmax_impl::DefaultType, | |
154 typename R2 = typename safe_minmax_impl::TypeOr< | |
155 R, | |
156 typename safe_minmax_impl::UnderlyingType< | |
kwiberg-webrtc
2017/04/13 00:56:05
I also changed it so that the return type is the u
| |
157 typename safe_minmax_impl::MType<T1, T2>::min_t>::type>::type> | |
158 constexpr R2 SafeMin(T1 a, T2 b) { | |
159 static_assert(IsIntlike<T1>::value || std::is_floating_point<T1>::value, | |
160 "The first argument must be integral or floating-point"); | |
161 static_assert(IsIntlike<T2>::value || std::is_floating_point<T2>::value, | |
162 "The second argument must be integral or floating-point"); | |
163 static_assert(IsIntlike<T1>::value == IsIntlike<T2>::value, | |
164 "You may not mix integral and floating-point arguments"); | |
165 return safe_cmp::Lt(a, b) ? static_cast<R2>(a) : static_cast<R2>(b); | |
166 } | |
167 | |
168 template <typename R = safe_minmax_impl::DefaultType, | |
169 typename T1 = safe_minmax_impl::DefaultType, | |
170 typename T2 = safe_minmax_impl::DefaultType, | |
171 typename R2 = typename safe_minmax_impl::TypeOr< | |
172 R, | |
173 typename safe_minmax_impl::UnderlyingType< | |
174 typename safe_minmax_impl::MType<T1, T2>::max_t>::type>::type> | |
175 constexpr R2 SafeMax(T1 a, T2 b) { | |
176 static_assert(IsIntlike<T1>::value || std::is_floating_point<T1>::value, | |
177 "The first argument must be integral or floating-point"); | |
178 static_assert(IsIntlike<T2>::value || std::is_floating_point<T2>::value, | |
179 "The second argument must be integral or floating-point"); | |
180 static_assert(IsIntlike<T1>::value == IsIntlike<T2>::value, | |
181 "You may not mix integral and floating-point arguments"); | |
182 return safe_cmp::Gt(a, b) ? static_cast<R2>(a) : static_cast<R2>(b); | |
183 } | |
184 | |
185 } // namespace rtc | |
186 | |
187 #endif // WEBRTC_BASE_SAFE_MINMAX_H_ | |
OLD | NEW |