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

Unified Diff: base/numerics/safe_math.h

Issue 2516153002: Move the remaining CheckedNumeric logic out of the macro (Closed)
Patch Set: nit Created 4 years, 1 month 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_math_impl.h » ('j') | base/numerics/safe_math_impl.h » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/numerics/safe_math.h
diff --git a/base/numerics/safe_math.h b/base/numerics/safe_math.h
index 511eb23f78763c929e86db014f08911d17d0acf2..73a50b2a549fd9d439f4a9ce59afa11275c900af 100644
--- a/base/numerics/safe_math.h
+++ b/base/numerics/safe_math.h
@@ -46,6 +46,25 @@ namespace internal {
// checked_size += HEADER LENGTH;
// if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size)
// Do stuff...
+
+template <typename T>
+class CheckedNumeric;
+
+// Used to treat CheckedNumeric and arithmetic underlying types the same.
+template <typename T>
+struct UnderlyingType {
+ using type = T;
+ static const bool is_numeric = std::is_arithmetic<T>::value;
+ static const bool is_checked = false;
+};
+
+template <typename T>
+struct UnderlyingType<CheckedNumeric<T>> {
+ using type = T;
+ static const bool is_numeric = true;
+ static const bool is_checked = true;
+};
+
template <typename T>
class CheckedNumeric {
static_assert(std::is_arithmetic<T>::value,
@@ -59,10 +78,10 @@ class CheckedNumeric {
// Copy constructor.
template <typename Src>
CheckedNumeric(const CheckedNumeric<Src>& rhs)
- : state_(rhs.ValueUnsafe(), rhs.IsValid()) {}
+ : state_(rhs.state_.value(), rhs.IsValid()) {}
template <typename Src>
- CheckedNumeric(Src value, bool is_valid) : state_(value, is_valid) {}
+ friend class CheckedNumeric;
// This is not an explicit constructor because we implicitly upgrade regular
// numerics to CheckedNumerics to make them easier to use.
@@ -102,30 +121,30 @@ class CheckedNumeric {
// crashing on a CHECK.
T ValueFloating() const {
static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float.");
- return CheckedNumeric<T>::cast(*this).ValueUnsafe();
+ return state_.value();
}
- // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now
- // for tests and to avoid a big matrix of friend operator overloads. But the
- // values it returns are unintuitive and likely to change in the future.
- // Returns: the raw numeric value, regardless of the current state.
- T ValueUnsafe() const { return state_.value(); }
+ // This friend method is available solely for providing more detailed logging
+ // in the the tests. Do not implement it in production code, because the
+ // underlying values may change at any time.
+ template <typename U>
+ friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
// Prototypes for the supported arithmetic operator overloads.
template <typename Src>
- CheckedNumeric& operator+=(Src rhs);
+ CheckedNumeric& operator+=(const Src rhs);
template <typename Src>
- CheckedNumeric& operator-=(Src rhs);
+ CheckedNumeric& operator-=(const Src rhs);
template <typename Src>
- CheckedNumeric& operator*=(Src rhs);
+ CheckedNumeric& operator*=(const Src rhs);
template <typename Src>
- CheckedNumeric& operator/=(Src rhs);
+ CheckedNumeric& operator/=(const Src rhs);
template <typename Src>
- CheckedNumeric& operator%=(Src rhs);
+ CheckedNumeric& operator%=(const Src rhs);
template <typename Src>
- CheckedNumeric& operator<<=(Src rhs);
+ CheckedNumeric& operator<<=(const Src rhs);
template <typename Src>
- CheckedNumeric& operator>>=(Src rhs);
+ CheckedNumeric& operator>>=(const Src rhs);
CheckedNumeric operator-() const {
// Negation is always valid for floating point.
@@ -173,84 +192,83 @@ class CheckedNumeric {
return value;
}
- // These static methods behave like a convenience cast operator targeting
- // the desired CheckedNumeric type. As an optimization, a reference is
- // returned when Src is the same type as T.
- template <typename Src>
- static CheckedNumeric<T> cast(
- Src u,
- typename std::enable_if<std::numeric_limits<Src>::is_specialized,
- int>::type = 0) {
- return u;
- }
+ // These perform the actual math operations on the CheckedNumerics.
+ // Binary arithmetic operations.
+ template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+ static CheckedNumeric MathOp(const L& lhs, const R& rhs) {
+ using Math = M<typename UnderlyingType<L>::type,
+ typename UnderlyingType<R>::type, void>;
+ T result = 0;
+ bool is_valid =
+ Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
+ Math::Op(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
+ return CheckedNumeric<T>(result, is_valid);
+ };
+
+ // Assignment arithmetic operations.
+ template <template <typename, typename, typename> class M, typename R>
+ CheckedNumeric& MathOp(const R& rhs) {
+ using Math = M<T, typename UnderlyingType<R>::type, void>;
+ T result = 0; // Using T as the destination saves a range check.
+ bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
+ Math::Op(state_.value(), Wrapper<R>::value(rhs), &result);
+ *this = CheckedNumeric<T>(result, is_valid);
+ return *this;
+ };
+
+ private:
+ CheckedNumericState<T> state_;
template <typename Src>
- static CheckedNumeric<T> cast(
- const CheckedNumeric<Src>& u,
- typename std::enable_if<!std::is_same<Src, T>::value, int>::type = 0) {
- return u;
- }
+ CheckedNumeric(Src value, bool is_valid) : state_(value, is_valid) {}
- static const CheckedNumeric<T>& cast(const CheckedNumeric<T>& u) { return u; }
+ // These wrappers allow us to handle state the same way for both
+ // CheckedNumeric and POD arithmetic types.
+ template <typename Src>
+ struct Wrapper {
+ static bool is_valid(Src) { return true; }
+ static Src value(Src value) { return value; }
+ };
- private:
- CheckedNumericState<T> state_;
+ template <typename Src>
+ struct Wrapper<CheckedNumeric<Src>> {
+ static bool is_valid(const CheckedNumeric<Src>& v) { return v.IsValid(); }
+ static Src value(const CheckedNumeric<Src>& v) { return v.state_.value(); }
+ };
};
-// This is the boilerplate for the standard arithmetic operator overloads. A
-// macro isn't the prettiest solution, but it beats rewriting these five times.
-// Some details worth noting are:
-// * We apply the standard arithmetic promotions.
-// * We skip range checks for floating points.
-// * We skip range checks for destination integers with sufficient range.
-// TODO(jschuh): extract these out into templates.
-#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP, PROMOTION) \
- /* Binary arithmetic operator for CheckedNumerics of the same type. */ \
- template <typename L, typename R> \
- CheckedNumeric<typename ArithmeticPromotion<PROMOTION, L, R>::type> \
- operator OP(const CheckedNumeric<L>& lhs, const CheckedNumeric<R>& rhs) { \
- using P = typename ArithmeticPromotion<PROMOTION, L, R>::type; \
- if (!rhs.IsValid() || !lhs.IsValid()) \
- return CheckedNumeric<P>(0, false); \
- /* Floating point always takes the fast path */ \
- if (std::is_floating_point<L>::value || std::is_floating_point<R>::value) \
- return CheckedNumeric<P>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \
- P result = 0; \
- bool is_valid = \
- Checked##NAME(lhs.ValueUnsafe(), rhs.ValueUnsafe(), &result); \
- return CheckedNumeric<P>(result, is_valid); \
+// This is just boilerplate for the standard arithmetic operator overloads.
+// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
+#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \
+ /* Binary arithmetic operator for all CheckedNumeric operations. */ \
+ template <typename L, typename R, \
+ typename std::enable_if<UnderlyingType<L>::is_numeric && \
+ UnderlyingType<R>::is_numeric && \
+ (UnderlyingType<L>::is_checked || \
+ UnderlyingType<R>::is_checked)>::type* = \
+ nullptr> \
+ CheckedNumeric< \
+ typename Checked##NAME<typename UnderlyingType<L>::type, \
+ typename UnderlyingType<R>::type>::result_type> \
+ operator OP(const L lhs, const R rhs) { \
+ return decltype(lhs OP rhs)::template MathOp<Checked##NAME>(lhs, rhs); \
} \
/* Assignment arithmetic operator implementation from CheckedNumeric. */ \
template <typename L> \
template <typename R> \
- CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(R rhs) { \
- *this = *this OP rhs; \
- return *this; \
- } \
- /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \
- template <typename L, typename R, \
- typename std::enable_if<std::is_arithmetic<R>::value>::type* = \
- nullptr> \
- CheckedNumeric<typename ArithmeticPromotion<PROMOTION, L, R>::type> \
- operator OP(const CheckedNumeric<L>& lhs, R rhs) { \
- return lhs OP CheckedNumeric<R>(rhs); \
- } \
- /* Binary arithmetic operator for left numeric and right CheckedNumeric. */ \
- template <typename L, typename R, \
- typename std::enable_if<std::is_arithmetic<L>::value>::type* = \
- nullptr> \
- CheckedNumeric<typename ArithmeticPromotion<PROMOTION, L, R>::type> \
- operator OP(L lhs, const CheckedNumeric<R>& rhs) { \
- return CheckedNumeric<L>(lhs) OP rhs; \
+ CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) { \
+ return MathOp<Checked##NAME>(rhs); \
}
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=, MAX_EXPONENT_PROMOTION)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=, MAX_EXPONENT_PROMOTION)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=, MAX_EXPONENT_PROMOTION)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=, MAX_EXPONENT_PROMOTION)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=, MAX_EXPONENT_PROMOTION)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(LeftShift, <<, <<=, LEFT_PROMOTION)
-BASE_NUMERIC_ARITHMETIC_OPERATORS(RightShift, >>, >>=, LEFT_PROMOTION)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(LeftShift, <<, <<=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(RightShift, >>, >>=)
#undef BASE_NUMERIC_ARITHMETIC_OPERATORS
« no previous file with comments | « no previous file | base/numerics/safe_math_impl.h » ('j') | base/numerics/safe_math_impl.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698