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

Unified Diff: base/numerics/safe_math.h

Issue 2522073004: Add variadic helper functions for CheckedNumeric math operations (Closed)
Patch Set: compile fix 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') | no next file with comments »
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..598bd3e71847daf7fb46db9a07efe9c06452ede4 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
@@ -22,15 +21,41 @@ namespace internal {
// data types, and contains overloads for basic arithmetic operations (i.e.: +,
// -, *, /, %).
//
+// 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:
+// CheckAdd() - Addition.
+// CheckSub() - Subtraction.
+// CheckMul() - Multiplication.
+// CheckDiv() - Division.
+// CheckMod() - Modulous (integer only).
+// CheckLsh() - Left integer shift (integer only).
+// CheckRsh() - Right integer shift (integer only).
+//
+// The unary negation, increment, and decrement operators are supported, along
+// with the following unary arithmetic methods, which return a new
+// CheckedNumeric as a result of the operation:
+// Abs() - Absolute value.
+// UnsignedAbs() - Absolute value as an equival-width unsigned underlying type
+// (valid for only integral types).
+//
// The following methods convert from CheckedNumeric to standard numeric values:
-// IsValid() - Returns true if the underlying numeric value is valid (i.e. has
-// has not wrapped and is not the result of an invalid conversion).
-// 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.
-// ValueFloating() - Returns the underlying floating point value (valid only
-// only for floating point CheckedNumeric types).
+// IsValid() - Returns true if the underlying numeric value is valid (i.e. has
+// has not wrapped and is not the result of an invalid conversion).
+// 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 (will not trigger a CHECK).
+// ValueFloating() - Returns the underlying floating point value (valid only
+// for floating point CheckedNumeric types; will not cause a CHECK).
+//
+// The following are general utility methods that are useful for converting
+// between arithmetic types and CheckedNumeric types:
+// CheckedNumeric::Cast<Dst>() - Instance method returning a CheckedNumeric
+// derived from casting the current instance to a CheckedNumeric of
+// the supplied destination type.
+// CheckNum() - Creates a new CheckedNumeric from the underlying type of the
+// supplied arithmetic or CheckedNumeric type.
//
// Bitwise operations are explicitly not supported, because correct
// handling of some cases (e.g. sign manipulation) is ambiguous. Comparison
@@ -47,24 +72,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.");
@@ -122,6 +129,14 @@ class CheckedNumeric {
return state_.value();
}
+ // Returns a checked numeric of the specified type, cast from the current
+ // CheckedNumeric. If the current state is invalid or the destination cannot
+ // represent the result then the returned CheckedNumeric will be invalid.
+ template <typename Dst>
+ constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+ return *this;
+ }
+
// 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.
@@ -197,22 +212,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 +257,83 @@ 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".
+ typedef
+ typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type
+ type;
+};
+
+// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
+// or CheckedNumericType.
+template <typename T>
+CheckedNumeric<typename UnderlyingType<T>::type> CheckNum(T value) {
+ return value;
+}
+
+// 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> \
+ Check##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 +341,22 @@ 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::CheckNum;
+using internal::CheckAdd;
+using internal::CheckSub;
+using internal::CheckMul;
+using internal::CheckDiv;
+using internal::CheckMod;
+using internal::CheckLsh;
+using internal::CheckRsh;
} // namespace base
« no previous file with comments | « no previous file | base/numerics/safe_math_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698