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

Side by Side Diff: base/numerics/safe_math_impl.h

Issue 2522073004: Add variadic helper functions for CheckedNumeric math operations (Closed)
Patch Set: test fixes Created 4 years 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ 5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_
6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_ 6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_
7 7
8 #include <stddef.h> 8 #include <stddef.h>
9 #include <stdint.h> 9 #include <stdint.h>
10 10
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 // Addition is valid if the sign of (x + y) is equal to either that of x or 255 // Addition is valid if the sign of (x + y) is equal to either that of x or
256 // that of y. 256 // that of y.
257 return (std::numeric_limits<T>::is_signed) 257 return (std::numeric_limits<T>::is_signed)
258 ? HasSignBit(BinaryComplement( 258 ? HasSignBit(BinaryComplement(
259 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) 259 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))))
260 : (BinaryComplement(x) >= 260 : (BinaryComplement(x) >=
261 y); // Unsigned is either valid or underflow. 261 y); // Unsigned is either valid or underflow.
262 } 262 }
263 263
264 template <typename T, typename U, class Enable = void> 264 template <typename T, typename U, class Enable = void>
265 struct CheckedAdd {}; 265 struct CheckedAddOp {};
266 266
267 template <typename T, typename U> 267 template <typename T, typename U>
268 struct CheckedAdd< 268 struct CheckedAddOp<
269 T, 269 T,
270 U, 270 U,
271 typename std::enable_if<std::numeric_limits<T>::is_integer && 271 typename std::enable_if<std::numeric_limits<T>::is_integer &&
272 std::numeric_limits<U>::is_integer>::type> { 272 std::numeric_limits<U>::is_integer>::type> {
273 using result_type = 273 using result_type =
274 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; 274 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type;
275 template <typename V> 275 template <typename V>
276 static bool Op(T x, U y, V* result) { 276 static bool Do(T x, U y, V* result) {
277 #if USE_OVERFLOW_BUILTINS 277 #if USE_OVERFLOW_BUILTINS
278 return !__builtin_add_overflow(x, y, result); 278 return !__builtin_add_overflow(x, y, result);
279 #else 279 #else
280 using Promotion = 280 using Promotion =
281 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 281 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
282 Promotion presult; 282 Promotion presult;
283 // Fail if either operand is out of range for the promoted type. 283 // Fail if either operand is out of range for the promoted type.
284 // TODO(jschuh): This could be made to work for a broader range of values. 284 // TODO(jschuh): This could be made to work for a broader range of values.
285 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && 285 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
286 IsValueInRangeForNumericType<Promotion>(y); 286 IsValueInRangeForNumericType<Promotion>(y);
(...skipping 22 matching lines...) Expand all
309 *result = static_cast<T>(uresult); 309 *result = static_cast<T>(uresult);
310 // Subtraction is valid if either x and y have same sign, or (x-y) and x have 310 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
311 // the same sign. 311 // the same sign.
312 return (std::numeric_limits<T>::is_signed) 312 return (std::numeric_limits<T>::is_signed)
313 ? HasSignBit(BinaryComplement( 313 ? HasSignBit(BinaryComplement(
314 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) 314 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))
315 : (x >= y); 315 : (x >= y);
316 } 316 }
317 317
318 template <typename T, typename U, class Enable = void> 318 template <typename T, typename U, class Enable = void>
319 struct CheckedSub {}; 319 struct CheckedSubOp {};
320 320
321 template <typename T, typename U> 321 template <typename T, typename U>
322 struct CheckedSub< 322 struct CheckedSubOp<
323 T, 323 T,
324 U, 324 U,
325 typename std::enable_if<std::numeric_limits<T>::is_integer && 325 typename std::enable_if<std::numeric_limits<T>::is_integer &&
326 std::numeric_limits<U>::is_integer>::type> { 326 std::numeric_limits<U>::is_integer>::type> {
327 using result_type = 327 using result_type =
328 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; 328 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type;
329 template <typename V> 329 template <typename V>
330 static bool Op(T x, U y, V* result) { 330 static bool Do(T x, U y, V* result) {
331 #if USE_OVERFLOW_BUILTINS 331 #if USE_OVERFLOW_BUILTINS
332 return !__builtin_sub_overflow(x, y, result); 332 return !__builtin_sub_overflow(x, y, result);
333 #else 333 #else
334 using Promotion = 334 using Promotion =
335 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 335 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
336 Promotion presult; 336 Promotion presult;
337 // Fail if either operand is out of range for the promoted type. 337 // Fail if either operand is out of range for the promoted type.
338 // TODO(jschuh): This could be made to work for a broader range of values. 338 // TODO(jschuh): This could be made to work for a broader range of values.
339 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && 339 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
340 IsValueInRangeForNumericType<Promotion>(y); 340 IsValueInRangeForNumericType<Promotion>(y);
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
400 typename std::enable_if<std::numeric_limits<T>::is_integer && 400 typename std::enable_if<std::numeric_limits<T>::is_integer &&
401 !std::numeric_limits<T>::is_signed && 401 !std::numeric_limits<T>::is_signed &&
402 (sizeof(T) * 2 > sizeof(uintmax_t)), 402 (sizeof(T) * 2 > sizeof(uintmax_t)),
403 bool>::type 403 bool>::type
404 CheckedMulImpl(T x, T y, T* result) { 404 CheckedMulImpl(T x, T y, T* result) {
405 *result = x * y; 405 *result = x * y;
406 return (y == 0 || x <= std::numeric_limits<T>::max() / y); 406 return (y == 0 || x <= std::numeric_limits<T>::max() / y);
407 } 407 }
408 408
409 template <typename T, typename U, class Enable = void> 409 template <typename T, typename U, class Enable = void>
410 struct CheckedMul {}; 410 struct CheckedMulOp {};
411 411
412 template <typename T, typename U> 412 template <typename T, typename U>
413 struct CheckedMul< 413 struct CheckedMulOp<
414 T, 414 T,
415 U, 415 U,
416 typename std::enable_if<std::numeric_limits<T>::is_integer && 416 typename std::enable_if<std::numeric_limits<T>::is_integer &&
417 std::numeric_limits<U>::is_integer>::type> { 417 std::numeric_limits<U>::is_integer>::type> {
418 using result_type = 418 using result_type =
419 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; 419 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type;
420 template <typename V> 420 template <typename V>
421 static bool Op(T x, U y, V* result) { 421 static bool Do(T x, U y, V* result) {
422 #if USE_OVERFLOW_BUILTINS 422 #if USE_OVERFLOW_BUILTINS
423 #if defined(__clang__) 423 #if defined(__clang__)
424 // TODO(jschuh): Get the Clang runtime library issues sorted out so we can 424 // TODO(jschuh): Get the Clang runtime library issues sorted out so we can
425 // support full-width, mixed-sign multiply builtins. 425 // support full-width, mixed-sign multiply builtins.
426 // https://crbug.com/613003 426 // https://crbug.com/613003
427 static const bool kUseMaxInt = 427 static const bool kUseMaxInt =
428 sizeof(__typeof__(x * y)) < sizeof(intptr_t) || 428 sizeof(__typeof__(x * y)) < sizeof(intptr_t) ||
429 (sizeof(__typeof__(x * y)) == sizeof(intptr_t) && 429 (sizeof(__typeof__(x * y)) == sizeof(intptr_t) &&
430 std::is_signed<T>::value == std::is_signed<U>::value); 430 std::is_signed<T>::value == std::is_signed<U>::value);
431 #else 431 #else
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
463 CheckedDivImpl(T x, T y, T* result) { 463 CheckedDivImpl(T x, T y, T* result) {
464 if (y && (!std::numeric_limits<T>::is_signed || 464 if (y && (!std::numeric_limits<T>::is_signed ||
465 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { 465 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) {
466 *result = x / y; 466 *result = x / y;
467 return true; 467 return true;
468 } 468 }
469 return false; 469 return false;
470 } 470 }
471 471
472 template <typename T, typename U, class Enable = void> 472 template <typename T, typename U, class Enable = void>
473 struct CheckedDiv {}; 473 struct CheckedDivOp {};
474 474
475 template <typename T, typename U> 475 template <typename T, typename U>
476 struct CheckedDiv< 476 struct CheckedDivOp<
477 T, 477 T,
478 U, 478 U,
479 typename std::enable_if<std::numeric_limits<T>::is_integer && 479 typename std::enable_if<std::numeric_limits<T>::is_integer &&
480 std::numeric_limits<U>::is_integer>::type> { 480 std::numeric_limits<U>::is_integer>::type> {
481 using result_type = 481 using result_type =
482 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; 482 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type;
483 template <typename V> 483 template <typename V>
484 static bool Op(T x, U y, V* result) { 484 static bool Do(T x, U y, V* result) {
485 using Promotion = 485 using Promotion =
486 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 486 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
487 Promotion presult; 487 Promotion presult;
488 // Fail if either operand is out of range for the promoted type. 488 // Fail if either operand is out of range for the promoted type.
489 // TODO(jschuh): This could be made to work for a broader range of values. 489 // TODO(jschuh): This could be made to work for a broader range of values.
490 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && 490 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
491 IsValueInRangeForNumericType<Promotion>(y); 491 IsValueInRangeForNumericType<Promotion>(y);
492 is_valid &= CheckedDivImpl(static_cast<Promotion>(x), 492 is_valid &= CheckedDivImpl(static_cast<Promotion>(x),
493 static_cast<Promotion>(y), &presult); 493 static_cast<Promotion>(y), &presult);
494 *result = static_cast<V>(presult); 494 *result = static_cast<V>(presult);
(...skipping 19 matching lines...) Expand all
514 bool>::type 514 bool>::type
515 CheckedModImpl(T x, T y, T* result) { 515 CheckedModImpl(T x, T y, T* result) {
516 if (y != 0) { 516 if (y != 0) {
517 *result = static_cast<T>(x % y); 517 *result = static_cast<T>(x % y);
518 return true; 518 return true;
519 } 519 }
520 return false; 520 return false;
521 } 521 }
522 522
523 template <typename T, typename U, class Enable = void> 523 template <typename T, typename U, class Enable = void>
524 struct CheckedMod {}; 524 struct CheckedModOp {};
525 525
526 template <typename T, typename U> 526 template <typename T, typename U>
527 struct CheckedMod< 527 struct CheckedModOp<
528 T, 528 T,
529 U, 529 U,
530 typename std::enable_if<std::numeric_limits<T>::is_integer && 530 typename std::enable_if<std::numeric_limits<T>::is_integer &&
531 std::numeric_limits<U>::is_integer>::type> { 531 std::numeric_limits<U>::is_integer>::type> {
532 using result_type = 532 using result_type =
533 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; 533 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type;
534 template <typename V> 534 template <typename V>
535 static bool Op(T x, U y, V* result) { 535 static bool Do(T x, U y, V* result) {
536 using Promotion = 536 using Promotion =
537 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; 537 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
538 Promotion presult; 538 Promotion presult;
539 bool is_valid = CheckedModImpl(static_cast<Promotion>(x), 539 bool is_valid = CheckedModImpl(static_cast<Promotion>(x),
540 static_cast<Promotion>(y), &presult); 540 static_cast<Promotion>(y), &presult);
541 *result = static_cast<V>(presult); 541 *result = static_cast<V>(presult);
542 return is_valid && IsValueInRangeForNumericType<V>(presult); 542 return is_valid && IsValueInRangeForNumericType<V>(presult);
543 } 543 }
544 }; 544 };
545 545
546 template <typename T, typename U, class Enable = void> 546 template <typename T, typename U, class Enable = void>
547 struct CheckedLeftShift {}; 547 struct CheckedLshOp {};
548 548
549 // Left shift. Shifts less than 0 or greater than or equal to the number 549 // Left shift. Shifts less than 0 or greater than or equal to the number
550 // of bits in the promoted type are undefined. Shifts of negative values 550 // of bits in the promoted type are undefined. Shifts of negative values
551 // are undefined. Otherwise it is defined when the result fits. 551 // are undefined. Otherwise it is defined when the result fits.
552 template <typename T, typename U> 552 template <typename T, typename U>
553 struct CheckedLeftShift< 553 struct CheckedLshOp<
554 T, 554 T,
555 U, 555 U,
556 typename std::enable_if<std::numeric_limits<T>::is_integer && 556 typename std::enable_if<std::numeric_limits<T>::is_integer &&
557 std::numeric_limits<U>::is_integer>::type> { 557 std::numeric_limits<U>::is_integer>::type> {
558 using result_type = T; 558 using result_type = T;
559 template <typename V> 559 template <typename V>
560 static bool Op(T x, U shift, V* result) { 560 static bool Do(T x, U shift, V* result) {
561 using ShiftType = typename UnsignedIntegerForSize<T>::type; 561 using ShiftType = typename UnsignedIntegerForSize<T>::type;
562 static const ShiftType kBitWidth = CHAR_BIT * sizeof(T); 562 static const ShiftType kBitWidth = CHAR_BIT * sizeof(T);
563 const ShiftType real_shift = static_cast<ShiftType>(shift); 563 const ShiftType real_shift = static_cast<ShiftType>(shift);
564 // Signed shift is not legal on negative values. 564 // Signed shift is not legal on negative values.
565 if (!IsValueNegative(x) && real_shift < kBitWidth) { 565 if (!IsValueNegative(x) && real_shift < kBitWidth) {
566 // Just use a multiplication because it's easy. 566 // Just use a multiplication because it's easy.
567 // TODO(jschuh): This could probably be made more efficient. 567 // TODO(jschuh): This could probably be made more efficient.
568 if (!std::is_signed<T>::value || real_shift != kBitWidth - 1) 568 if (!std::is_signed<T>::value || real_shift != kBitWidth - 1)
569 return CheckedMul<T, T>::Op(x, static_cast<T>(1) << shift, result); 569 return CheckedMulOp<T, T>::Do(x, static_cast<T>(1) << shift, result);
570 return !x; // Special case zero for a full width signed shift. 570 return !x; // Special case zero for a full width signed shift.
571 } 571 }
572 return false; 572 return false;
573 } 573 }
574 }; 574 };
575 575
576 template <typename T, typename U, class Enable = void> 576 template <typename T, typename U, class Enable = void>
577 struct CheckedRightShift {}; 577 struct CheckedRshOp {};
578 578
579 // Right shift. Shifts less than 0 or greater than or equal to the number 579 // Right shift. Shifts less than 0 or greater than or equal to the number
580 // of bits in the promoted type are undefined. Otherwise, it is always defined, 580 // of bits in the promoted type are undefined. Otherwise, it is always defined,
581 // but a right shift of a negative value is implementation-dependent. 581 // but a right shift of a negative value is implementation-dependent.
582 template <typename T, typename U> 582 template <typename T, typename U>
583 struct CheckedRightShift< 583 struct CheckedRshOp<
584 T, 584 T,
585 U, 585 U,
586 typename std::enable_if<std::numeric_limits<T>::is_integer && 586 typename std::enable_if<std::numeric_limits<T>::is_integer &&
587 std::numeric_limits<U>::is_integer>::type> { 587 std::numeric_limits<U>::is_integer>::type> {
588 using result_type = T; 588 using result_type = T;
589 template <typename V = result_type> 589 template <typename V = result_type>
590 static bool Op(T x, U shift, V* result) { 590 static bool Do(T x, U shift, V* result) {
591 // Use the type conversion push negative values out of range. 591 // Use the type conversion push negative values out of range.
592 using ShiftType = typename UnsignedIntegerForSize<T>::type; 592 using ShiftType = typename UnsignedIntegerForSize<T>::type;
593 if (static_cast<ShiftType>(shift) < (CHAR_BIT * sizeof(T))) { 593 if (static_cast<ShiftType>(shift) < (CHAR_BIT * sizeof(T))) {
594 T tmp = x >> shift; 594 T tmp = x >> shift;
595 *result = static_cast<V>(tmp); 595 *result = static_cast<V>(tmp);
596 return IsValueInRangeForNumericType<V>(tmp); 596 return IsValueInRangeForNumericType<V>(tmp);
597 } 597 }
598 return false; 598 return false;
599 } 599 }
600 }; 600 };
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
662 constexpr typename std::enable_if<std::numeric_limits<T>::is_integer && 662 constexpr typename std::enable_if<std::numeric_limits<T>::is_integer &&
663 !std::numeric_limits<T>::is_signed, 663 !std::numeric_limits<T>::is_signed,
664 T>::type 664 T>::type
665 SafeUnsignedAbs(T value) { 665 SafeUnsignedAbs(T value) {
666 // T is unsigned, so |value| must already be positive. 666 // T is unsigned, so |value| must already be positive.
667 return static_cast<T>(value); 667 return static_cast<T>(value);
668 } 668 }
669 669
670 // This is just boilerplate that wraps the standard floating point arithmetic. 670 // This is just boilerplate that wraps the standard floating point arithmetic.
671 // A macro isn't the nicest solution, but it beats rewriting these repeatedly. 671 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
672 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ 672 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
673 template <typename T, typename U> \ 673 template <typename T, typename U> \
674 struct Checked##NAME<T, U, typename std::enable_if< \ 674 struct Checked##NAME##Op< \
675 std::numeric_limits<T>::is_iec559 || \ 675 T, U, \
676 std::numeric_limits<U>::is_iec559>::type> { \ 676 typename std::enable_if<std::numeric_limits<T>::is_iec559 || \
677 using result_type = \ 677 std::numeric_limits<U>::is_iec559>::type> { \
678 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; \ 678 using result_type = \
679 template <typename V> \ 679 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; \
680 static bool Op(T x, U y, V* result) { \ 680 template <typename V> \
681 using Promotion = \ 681 static bool Do(T x, U y, V* result) { \
682 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; \ 682 using Promotion = \
683 Promotion presult = x OP y; \ 683 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; \
684 *result = static_cast<V>(presult); \ 684 Promotion presult = x OP y; \
685 return IsValueInRangeForNumericType<V>(presult); \ 685 *result = static_cast<V>(presult); \
686 } \ 686 return IsValueInRangeForNumericType<V>(presult); \
687 } \
687 }; 688 };
688 689
689 BASE_FLOAT_ARITHMETIC_OPS(Add, +) 690 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
690 BASE_FLOAT_ARITHMETIC_OPS(Sub, -) 691 BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
691 BASE_FLOAT_ARITHMETIC_OPS(Mul, *) 692 BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
692 BASE_FLOAT_ARITHMETIC_OPS(Div, /) 693 BASE_FLOAT_ARITHMETIC_OPS(Div, /)
693 694
694 #undef BASE_FLOAT_ARITHMETIC_OPS 695 #undef BASE_FLOAT_ARITHMETIC_OPS
695 696
696 template <typename T> 697 template <typename T>
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
798 799
799 // Copy constructor. 800 // Copy constructor.
800 template <typename Src> 801 template <typename Src>
801 constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs) 802 constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
802 : value_(static_cast<T>(rhs.value())) {} 803 : value_(static_cast<T>(rhs.value())) {}
803 804
804 constexpr bool is_valid() const { return std::isfinite(value_); } 805 constexpr bool is_valid() const { return std::isfinite(value_); }
805 constexpr T value() const { return value_; } 806 constexpr T value() const { return value_; }
806 }; 807 };
807 808
809 // The following are helper templates used in the CheckedNumeric class.
810 template <typename T>
811 class CheckedNumeric;
812
813 // Used to treat CheckedNumeric and arithmetic underlying types the same.
814 template <typename T>
815 struct UnderlyingType {
816 using type = T;
817 static const bool is_numeric = std::is_arithmetic<T>::value;
818 static const bool is_checked = false;
819 };
820
821 template <typename T>
822 struct UnderlyingType<CheckedNumeric<T>> {
823 using type = T;
824 static const bool is_numeric = true;
825 static const bool is_checked = true;
826 };
827
828 template <typename L, typename R>
829 struct IsCheckedOp {
830 static const bool value =
831 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
832 (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
833 };
834
835 template <template <typename, typename, typename> class M,
836 typename L,
837 typename R>
838 struct MathWrapper {
839 using math = M<typename UnderlyingType<L>::type,
840 typename UnderlyingType<R>::type,
841 void>;
842 using type = typename math::result_type;
843 };
844
808 } // namespace internal 845 } // namespace internal
809 } // namespace base 846 } // namespace base
810 847
811 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ 848 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698