Chromium Code Reviews| Index: base/numerics/safe_conversions.h |
| diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h |
| index 51edac649c8ce59b59509041eec860e1ac2d499a..e4a16b3ac53aa1b6d0aed2db7637ac5470c2a8ad 100644 |
| --- a/base/numerics/safe_conversions.h |
| +++ b/base/numerics/safe_conversions.h |
| @@ -55,22 +55,6 @@ constexpr bool IsValueInRangeForNumericType(Src value) { |
| internal::RANGE_VALID; |
| } |
| -// Convenience function for determining if a numeric value is negative without |
| -// throwing compiler warnings on: unsigned(value) < 0. |
| -template <typename T, |
| - typename std::enable_if<std::is_signed<T>::value>::type* = nullptr> |
| -constexpr bool IsValueNegative(T value) { |
| - static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); |
| - return value < 0; |
| -} |
| - |
| -template <typename T, |
| - typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr> |
| -constexpr bool IsValueNegative(T) { |
| - static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); |
| - return false; |
| -} |
| - |
| // Forces a crash, like a CHECK(false). Used for numeric boundary errors. |
| struct CheckOnFailure { |
| template <typename T> |
| @@ -99,62 +83,55 @@ constexpr Dst checked_cast(Src value) { |
| : CheckHandler::template HandleFailure<Dst>(); |
| } |
| -// HandleNaN will return 0 in this case. |
| -struct SaturatedCastNaNBehaviorReturnZero { |
| - template <typename T> |
| - static constexpr T HandleFailure() { |
| - return T(); |
| +// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. |
| +template <typename T> |
| +struct SaturatedCastDefaultHandler { |
| + static constexpr T HandleNaN() { |
| + return std::numeric_limits<T>::has_quiet_NaN |
| + ? std::numeric_limits<T>::quiet_NaN() |
| + : T(); |
| + } |
| + static constexpr T max() { return std::numeric_limits<T>::max(); } |
| + static constexpr T HandleOverflow() { |
| + return std::numeric_limits<T>::has_infinity |
| + ? std::numeric_limits<T>::infinity() |
| + : std::numeric_limits<T>::max(); |
| + } |
| + static constexpr T lowest() { return std::numeric_limits<T>::lowest(); } |
| + static constexpr T HandleUnderflow() { |
| + return std::numeric_limits<T>::has_infinity |
| + ? std::numeric_limits<T>::infinity() * -1 |
| + : std::numeric_limits<T>::lowest(); |
| } |
| }; |
| namespace internal { |
| -// These wrappers are used for C++11 constexpr support by avoiding both the |
| -// declaration of local variables and invalid evaluation resulting from the |
| -// lack of "constexpr if" support in the saturated_cast template function. |
| -// TODO(jschuh): Convert to single function with a switch once we support C++14. |
| -template < |
| - typename Dst, |
| - class NaNHandler, |
| - typename Src, |
| - typename std::enable_if<std::is_integral<Dst>::value>::type* = nullptr> |
| -constexpr Dst saturated_cast_impl(const Src value, |
| - const RangeConstraint constraint) { |
| - return constraint == RANGE_VALID |
| - ? static_cast<Dst>(value) |
| - : (constraint == RANGE_UNDERFLOW |
| - ? std::numeric_limits<Dst>::lowest() |
| - : (constraint == RANGE_OVERFLOW |
| - ? std::numeric_limits<Dst>::max() |
| - : NaNHandler::template HandleFailure<Dst>())); |
| -} |
| - |
| -template <typename Dst, |
| - class NaNHandler, |
| - typename Src, |
| - typename std::enable_if<std::is_floating_point<Dst>::value>::type* = |
| - nullptr> |
| -constexpr Dst saturated_cast_impl(const Src value, |
| - const RangeConstraint constraint) { |
| - return constraint == RANGE_VALID |
| - ? static_cast<Dst>(value) |
| - : (constraint == RANGE_UNDERFLOW |
| - ? -std::numeric_limits<Dst>::infinity() |
| - : (constraint == RANGE_OVERFLOW |
| - ? std::numeric_limits<Dst>::infinity() |
| - : std::numeric_limits<Dst>::quiet_NaN())); |
| -} |
| - |
| // 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.
|
| -// that the specified numeric conversion will saturate rather than overflow or |
| -// underflow. NaN assignment to an integral will defer the behavior to a |
| -// specified class. By default, it will return 0. |
| +// that the specified numeric conversion will saturate by default rather than |
| +// overflow or underflow, and NaN assignment to an integral will return 0. |
| +// All boundary condition behaviors can be overriden with a custom handler. |
| template <typename Dst, |
| - class NaNHandler = SaturatedCastNaNBehaviorReturnZero, |
| + template <typename> |
| + class SaturationHandler = SaturatedCastDefaultHandler, |
| typename Src> |
| constexpr Dst saturated_cast(Src value) { |
| + static_assert( |
| + SaturationHandler<Dst>::lowest() < SaturationHandler<Dst>::max(), ""); |
| using SrcType = typename UnderlyingType<Src>::type; |
| - return internal::saturated_cast_impl<Dst, NaNHandler>( |
| - value, internal::DstRangeRelationToSrcRange<Dst, SrcType>(value)); |
| + 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
|
| + value, NarrowingRange<Dst, SrcType, SaturationHandler>::lowest()) |
| + ? (IsLessOrEqual<SrcType, Dst>::Test( |
| + value, |
| + NarrowingRange<Dst, SrcType, SaturationHandler>::max()) |
| + ? static_cast<Dst>(value) |
| + : SaturationHandler<Dst>::HandleOverflow()) |
| + : (std::is_integral<SrcType>::value || |
| + std::is_floating_point<Dst>::value || |
| + IsLessOrEqual<SrcType, Dst>::Test( |
| + value, NarrowingRange<Dst, SrcType, |
| + SaturationHandler>::max()) |
| + ? SaturationHandler<Dst>::HandleUnderflow() |
| + : SaturationHandler<Dst>::HandleNaN()); |
| } |
| // strict_cast<> is analogous to static_cast<> for numeric types, except that |
| @@ -285,6 +262,7 @@ using internal::saturated_cast; |
| using internal::SafeUnsignedAbs; |
| using internal::StrictNumeric; |
| using internal::MakeStrictNum; |
| +using internal::IsValueNegative; |
| // Explicitly make a shorter size_t alias for convenience. |
| using SizeT = StrictNumeric<size_t>; |