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

Unified Diff: base/numerics/safe_conversions.h

Issue 2578613002: Support saturation overrides in saturated_cast (Closed)
Patch Set: build 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | base/numerics/safe_conversions_impl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/numerics/safe_conversions.h
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h
index 51edac649c8ce59b59509041eec860e1ac2d499a..66b278ff3d3e41b3ce33f1d56bea183f12dc7b61 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,62 @@ 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
-// 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(), "");
+ // While this looks like a lot of code, it's all constexpr and all but
+ // one variable are compile-time constants (enforced by a static_assert).
+ // So, it should evaluate to the minimum number of comparisons required
+ // for the range check, which is 0-3, depending on the exact source and
+ // destination types, and whatever custom range is specified.
using SrcType = typename UnderlyingType<Src>::type;
- return internal::saturated_cast_impl<Dst, NaNHandler>(
- value, internal::DstRangeRelationToSrcRange<Dst, SrcType>(value));
+ return IsGreaterOrEqual<SrcType, Dst>::Test(
+ value, NarrowingRange<Dst, SrcType, SaturationHandler>::lowest())
+ ? (IsLessOrEqual<SrcType, Dst>::Test(
+ value,
+ NarrowingRange<Dst, SrcType, SaturationHandler>::max())
+ ? static_cast<Dst>(value)
+ : SaturationHandler<Dst>::HandleOverflow())
+ // This last branch is a little confusing. It's specifically to
+ // catch NaN when converting from float to integral.
+ : (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 +269,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>;
« no previous file with comments | « no previous file | base/numerics/safe_conversions_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698