Chromium Code Reviews| 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; |