Index: base/numerics/safe_conversions.h |
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h |
index 4b9129dfffe94329c954bc1842069e7e5bd1e919..04e4d446230000dd817d90249d9951d8e155423d 100644 |
--- a/base/numerics/safe_conversions.h |
+++ b/base/numerics/safe_conversions.h |
@@ -6,9 +6,11 @@ |
#define BASE_NUMERICS_SAFE_CONVERSIONS_H_ |
#include <stddef.h> |
+#include <stdlib.h> |
#include <cassert> |
#include <limits> |
+#include <ostream> |
#include <type_traits> |
#include "base/numerics/safe_conversions_impl.h" |
@@ -91,7 +93,6 @@ constexpr Dst saturated_cast_impl(const Src value, |
? std::numeric_limits<Dst>::max() |
: NaNHandler::template HandleFailure<Dst>())); |
} |
-} // namespace internal |
// saturated_cast<> is analogous to static_cast<> for numeric types, except |
// that the specified numeric conversion will saturate rather than overflow or |
@@ -107,6 +108,37 @@ constexpr Dst saturated_cast(Src value) { |
value, internal::DstRangeRelationToSrcRange<Dst>(value)); |
} |
+// The following macro attempts to determine if the passed expression is a |
+// compile-time constant. GCC and clang have a compiler intrinsic for this, |
+// but MSVS needs to use a hack where compile-time expression evaluation will |
+// generate a zero constant that will trigger a different function overload |
+// than a compile-time variable would. |
+#if defined(__GNUC__) || defined(__clang__) |
+#define IS_COMPILE_TIME_CONSTANT(V) __builtin_constant_p(V) |
+#else |
+struct ConstTest { |
+ struct PlaceHolder { |
+ PlaceHolder(int64_t x) {} |
+ }; |
+ static int8_t Test(void*) { return int8_t(); } |
+ static int64_t Test(PlaceHolder) { return int64_t(); } |
+}; |
+ |
+#define IS_COMPILE_TIME_CONSTANT(V) \ |
+ (std::is_same<int8_t, decltype(internal::ConstTest::Test((V) - (V)))>::value) |
+#endif |
+ |
+// The test goes here because we undef the macro when we're done with it. |
+inline void TestCompileTimeConstantSupport() { |
+ const int kCompileTimeConstant = 10; |
+ int compile_time_variable = rand(); |
+ static_assert(IS_COMPILE_TIME_CONSTANT(std::numeric_limits<int>::is_signed), |
+ ""); |
+ static_assert(IS_COMPILE_TIME_CONSTANT(kCompileTimeConstant), ""); |
+ static_assert(!IS_COMPILE_TIME_CONSTANT(compile_time_variable), ""); |
+ (void)compile_time_variable; |
+} |
+ |
// strict_cast<> is analogous to static_cast<> for numeric types, except that |
// it will cause a compile failure if the destination type is not large enough |
// to contain any value in the source type. It performs no runtime checking. |
@@ -116,18 +148,20 @@ constexpr Dst strict_cast(Src value) { |
"Argument must be numeric."); |
static_assert(std::numeric_limits<Dst>::is_specialized, |
"Result must be numeric."); |
- static_assert((internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value == |
- internal::NUMERIC_RANGE_CONTAINED), |
- "The numeric conversion is out of range for this type. You " |
- "should probably use one of the following conversion " |
- "mechanisms on the value you want to pass:\n" |
- "- base::checked_cast\n" |
- "- base::saturated_cast\n" |
- "- base::CheckedNumeric"); |
+ |
+ // We try to make compile-time constants just work regardless of type. |
+ static_assert( |
+ IS_COMPILE_TIME_CONSTANT(value) |
+ ? IsValueInRangeForNumericType<Dst>(value) |
+ : internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value == |
+ internal::NUMERIC_RANGE_CONTAINED, |
+ "The source type is out of range for the destination type"); |
return static_cast<Dst>(value); |
} |
+#undef IS_COMPILE_TIME_CONSTANT |
+ |
// StrictNumeric implements compile time range checking between numeric types by |
// wrapping assignment operations in a strict_cast. This class is intended to be |
// used for function arguments and return types, to ensure the destination type |
@@ -157,15 +191,57 @@ class StrictNumeric { |
: value_(strict_cast<T>(value)) {} |
// The numeric cast operator basically handles all the magic. |
- template <typename Dst> |
+ template <typename Dst, |
+ typename std::enable_if< |
+ ArithmeticOrUnderlyingEnum<Dst>::value>::type* = nullptr> |
constexpr operator Dst() const { |
- return strict_cast<Dst>(value_); |
+ return strict_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_); |
} |
private: |
const T value_; |
}; |
+template <typename T> |
+std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { |
+ os << static_cast<T>(value); |
+ return os; |
+} |
+ |
+// We allow simple pointer arithmetic. |
Tom Sepez
2016/11/29 20:06:26
For extra credit: Can we make this blow up if R is
jschuh
2016/11/29 21:04:47
It would be very easy to just call the correspondi
jschuh
2016/11/30 23:38:52
Okay, I decided that wrapping here is always unsaf
|
+template <typename L, typename R> |
+constexpr L* operator+(L* lhs, const StrictNumeric<R>& rhs) { |
+ return lhs + static_cast<R>(rhs); |
+} |
+ |
+template <typename L, typename R> |
+constexpr L* operator-(L* lhs, const StrictNumeric<R>& rhs) { |
+ return lhs - static_cast<R>(rhs); |
+} |
+ |
+#define STRICT_COMPARISON_OP(NAME, OP) \ |
+ template <typename L, typename R, \ |
+ typename std::enable_if< \ |
+ internal::IsStrictOp<L, R>::value>::type* = nullptr> \ |
+ constexpr bool operator OP(const L lhs, const R rhs) { \ |
+ return SafeCompare<NAME, typename UnderlyingType<L>::type, \ |
+ typename UnderlyingType<R>::type>(lhs, rhs); \ |
+ } |
+ |
+STRICT_COMPARISON_OP(IsLess, <); |
+STRICT_COMPARISON_OP(IsLessOrEqual, <=); |
+STRICT_COMPARISON_OP(IsGreater, >); |
+STRICT_COMPARISON_OP(IsGreaterOrEqual, >=); |
+STRICT_COMPARISON_OP(IsEqual, ==); |
+STRICT_COMPARISON_OP(IsNotEqual, !=); |
+ |
+#undef STRICT_COMPARISON_OP |
+}; |
+ |
+using internal::strict_cast; |
+using internal::saturated_cast; |
+using internal::StrictNumeric; |
+ |
// Explicitly make a shorter size_t typedef for convenience. |
typedef StrictNumeric<size_t> SizeT; |