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

Unified Diff: base/numerics/safe_math.h

Issue 2522073004: Add variadic helper functions for CheckedNumeric math operations (Closed)
Patch Set: test fixes 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_numerics_unittest.cc » ('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 470f4666981cc3788f85e7137807e31b4a1a79ec..dea944c34768f65deaa2bcf0ed6efdb8b0713e55 100644
--- a/base/numerics/safe_math.h
+++ b/base/numerics/safe_math.h
@@ -13,7 +13,6 @@
#include "base/numerics/safe_math_impl.h"
namespace base {
-
namespace internal {
// CheckedNumeric implements all the logic and operators for detecting integer
@@ -28,9 +27,21 @@ namespace internal {
// ValueOrDie() - Returns the underlying value. If the state is not valid this
// call will crash on a CHECK.
// ValueOrDefault() - Returns the current value, or the supplied default if the
-// state is not valid.
+// state is not valid (will not trigger a CHECK).
// ValueFloating() - Returns the underlying floating point value (valid only
-// only for floating point CheckedNumeric types).
+// only for floating point CheckedNumeric types; will not
+// trigger a CHECK).
+//
+// You may also use one of the variadic convenience functions, which accept
+// standard arithmetic or CheckedNumeric types, perform arithmetic operations,
+// and return a CheckedNumeric result. The supported functions are:
+// * CheckedAdd() - addition
Tom Sepez 2016/11/23 16:54:58 Can I explicitly specialize these, for instance if
jschuh 2016/11/23 19:18:00 That's a good idea. I can add it.
Tom Sepez 2016/11/23 19:47:08 I guess I worry about something getting silently p
Tom Sepez 2016/11/23 19:47:08 Yeah, I kinda worry about something getting silent
jschuh 2016/11/23 21:49:22 Okay, we might have a miscommunication here. I hav
+// * CheckedSub() - subtraction
+// * CheckedMul() - multiplication
+// * CheckedDiv() - division
+// * CheckedMod() - modulous (integer only)
+// * CheckedLsh() - left integer shift (integer only)
+// * CheckedRsh() - right integer shift (integer only)
//
// Bitwise operations are explicitly not supported, because correct
// handling of some cases (e.g. sign manipulation) is ambiguous. Comparison
@@ -47,24 +58,6 @@ namespace internal {
// 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,
"CheckedNumeric<T>: T must be a numeric type.");
@@ -197,22 +190,21 @@ class CheckedNumeric {
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>;
+ using Math = typename MathWrapper<M, L, R>::math;
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);
+ Math::Do(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>;
+ using Math = typename MathWrapper<M, T, R>::math;
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);
+ Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
*this = CheckedNumeric<T>(result, is_valid);
return *this;
};
@@ -243,27 +235,76 @@ class CheckedNumeric {
};
};
+// These variadic templates work out the return types.
+// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+struct ResultType;
+
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+struct ResultType<M, L, R> {
+ using type = typename MathWrapper<M, L, R>::type;
+};
+
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+struct ResultType {
+ // The typedef was required here because MSVC fails to compile with "using".
Tom Sepez 2016/11/23 16:52:06 Let's get Bruce to weigh in on this one - at least
jschuh 2016/11/23 19:18:00 I've learned that any complex type resolution with
+ typedef
+ typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type
+ type;
+};
+
+// These implement the variadic wrapper for the math operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+CheckedNumeric<typename MathWrapper<M, L, R>::type> ChkMathOp(const L lhs,
+ const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
+ rhs);
+};
+
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
+ChkMathOp(const L lhs, const R rhs, const Args... args) {
+ auto tmp = ChkMathOp<M>(lhs, rhs);
+ return tmp.IsValid() ? ChkMathOp<M>(tmp, args...)
+ : decltype(ChkMathOp<M>(tmp, args...))(tmp);
+};
+
// 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(const R rhs) { \
- return MathOp<Checked##NAME>(rhs); \
+#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<IsCheckedOp<L, R>::value>::type* = \
+ nullptr> \
+ CheckedNumeric<typename MathWrapper<Checked##NAME##Op, L, R>::type> \
+ operator OP(const L lhs, const R rhs) { \
+ return decltype(lhs OP rhs)::template MathOp<Checked##NAME##Op>(lhs, rhs); \
+ } \
+ /* Assignment arithmetic operator implementation from CheckedNumeric. */ \
+ template <typename L> \
+ template <typename R> \
+ CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) { \
+ return MathOp<Checked##NAME##Op>(rhs); \
+ } \
+ /* Variadic arithmetic functions that return CheckedNumeric. */ \
+ template <typename L, typename R, typename... Args> \
+ CheckedNumeric<typename ResultType<Checked##NAME##Op, L, R, Args...>::type> \
+ Checked##NAME(const L lhs, const R rhs, const Args... args) { \
+ return ChkMathOp<Checked##NAME##Op, L, R, Args...>(lhs, rhs, args...); \
}
BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=)
@@ -271,14 +312,21 @@ 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, >>, >>=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Lsh, <<, <<=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Rsh, >>, >>=)
#undef BASE_NUMERIC_ARITHMETIC_OPERATORS
} // namespace internal
using internal::CheckedNumeric;
+using internal::CheckedAdd;
+using internal::CheckedSub;
+using internal::CheckedMul;
+using internal::CheckedDiv;
+using internal::CheckedMod;
+using internal::CheckedLsh;
+using internal::CheckedRsh;
} // namespace base
« no previous file with comments | « no previous file | base/numerics/safe_math_impl.h » ('j') | base/numerics/safe_numerics_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698