Home | History | Annotate | Download | only in numerics
      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
      3 // found in the LICENSE file.
      4 
      5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_
      6 #define BASE_NUMERICS_SAFE_CONVERSIONS_H_
      7 
      8 #include <stddef.h>
      9 
     10 #include <limits>
     11 #include <type_traits>
     12 
     13 #include "base/logging.h"
     14 #include "base/numerics/safe_conversions_impl.h"
     15 
     16 namespace base {
     17 
     18 // Convenience function that returns true if the supplied value is in range
     19 // for the destination type.
     20 template <typename Dst, typename Src>
     21 constexpr bool IsValueInRangeForNumericType(Src value) {
     22   return internal::DstRangeRelationToSrcRange<Dst>(value) ==
     23          internal::RANGE_VALID;
     24 }
     25 
     26 // Convenience function for determining if a numeric value is negative without
     27 // throwing compiler warnings on: unsigned(value) < 0.
     28 template <typename T>
     29 constexpr typename std::enable_if<std::numeric_limits<T>::is_signed, bool>::type
     30 IsValueNegative(T value) {
     31   static_assert(std::numeric_limits<T>::is_specialized,
     32                 "Argument must be numeric.");
     33   return value < 0;
     34 }
     35 
     36 template <typename T>
     37 constexpr typename std::enable_if<!std::numeric_limits<T>::is_signed,
     38                                   bool>::type IsValueNegative(T) {
     39   static_assert(std::numeric_limits<T>::is_specialized,
     40                 "Argument must be numeric.");
     41   return false;
     42 }
     43 
     44 // checked_cast<> is analogous to static_cast<> for numeric types,
     45 // except that it CHECKs that the specified numeric conversion will not
     46 // overflow or underflow. NaN source will always trigger a CHECK.
     47 template <typename Dst, typename Src>
     48 inline Dst checked_cast(Src value) {
     49   CHECK(IsValueInRangeForNumericType<Dst>(value));
     50   return static_cast<Dst>(value);
     51 }
     52 
     53 // HandleNaN will cause this class to CHECK(false).
     54 struct SaturatedCastNaNBehaviorCheck {
     55   template <typename T>
     56   static T HandleNaN() {
     57     CHECK(false);
     58     return T();
     59   }
     60 };
     61 
     62 // HandleNaN will return 0 in this case.
     63 struct SaturatedCastNaNBehaviorReturnZero {
     64   template <typename T>
     65   static constexpr T HandleNaN() {
     66     return T();
     67   }
     68 };
     69 
     70 namespace internal {
     71 // This wrapper is used for C++11 constexpr support by avoiding the declaration
     72 // of local variables in the saturated_cast template function.
     73 template <typename Dst, class NaNHandler, typename Src>
     74 constexpr Dst saturated_cast_impl(const Src value,
     75                                   const RangeConstraint constraint) {
     76   return constraint == RANGE_VALID
     77              ? static_cast<Dst>(value)
     78              : (constraint == RANGE_UNDERFLOW
     79                     ? std::numeric_limits<Dst>::min()
     80                     : (constraint == RANGE_OVERFLOW
     81                            ? std::numeric_limits<Dst>::max()
     82                            : (constraint == RANGE_INVALID
     83                                   ? NaNHandler::template HandleNaN<Dst>()
     84                                   : (NOTREACHED(), static_cast<Dst>(value)))));
     85 }
     86 }  // namespace internal
     87 
     88 // saturated_cast<> is analogous to static_cast<> for numeric types, except
     89 // that the specified numeric conversion will saturate rather than overflow or
     90 // underflow. NaN assignment to an integral will defer the behavior to a
     91 // specified class. By default, it will return 0.
     92 template <typename Dst,
     93           class NaNHandler = SaturatedCastNaNBehaviorReturnZero,
     94           typename Src>
     95 constexpr Dst saturated_cast(Src value) {
     96   return std::numeric_limits<Dst>::is_iec559
     97              ? static_cast<Dst>(value)  // Floating point optimization.
     98              : internal::saturated_cast_impl<Dst, NaNHandler>(
     99                    value, internal::DstRangeRelationToSrcRange<Dst>(value));
    100 }
    101 
    102 // strict_cast<> is analogous to static_cast<> for numeric types, except that
    103 // it will cause a compile failure if the destination type is not large enough
    104 // to contain any value in the source type. It performs no runtime checking.
    105 template <typename Dst, typename Src>
    106 constexpr Dst strict_cast(Src value) {
    107   static_assert(std::numeric_limits<Src>::is_specialized,
    108                 "Argument must be numeric.");
    109   static_assert(std::numeric_limits<Dst>::is_specialized,
    110                 "Result must be numeric.");
    111   static_assert((internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
    112                  internal::NUMERIC_RANGE_CONTAINED),
    113                 "The numeric conversion is out of range for this type. You "
    114                 "should probably use one of the following conversion "
    115                 "mechanisms on the value you want to pass:\n"
    116                 "- base::checked_cast\n"
    117                 "- base::saturated_cast\n"
    118                 "- base::CheckedNumeric");
    119 
    120   return static_cast<Dst>(value);
    121 }
    122 
    123 // StrictNumeric implements compile time range checking between numeric types by
    124 // wrapping assignment operations in a strict_cast. This class is intended to be
    125 // used for function arguments and return types, to ensure the destination type
    126 // can always contain the source type. This is essentially the same as enforcing
    127 // -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied
    128 // incrementally at API boundaries, making it easier to convert code so that it
    129 // compiles cleanly with truncation warnings enabled.
    130 // This template should introduce no runtime overhead, but it also provides no
    131 // runtime checking of any of the associated mathematical operations. Use
    132 // CheckedNumeric for runtime range checks of the actual value being assigned.
    133 template <typename T>
    134 class StrictNumeric {
    135  public:
    136   typedef T type;
    137 
    138   constexpr StrictNumeric() : value_(0) {}
    139 
    140   // Copy constructor.
    141   template <typename Src>
    142   constexpr StrictNumeric(const StrictNumeric<Src>& rhs)
    143       : value_(strict_cast<T>(rhs.value_)) {}
    144 
    145   // This is not an explicit constructor because we implicitly upgrade regular
    146   // numerics to StrictNumerics to make them easier to use.
    147   template <typename Src>
    148   constexpr StrictNumeric(Src value)
    149       : value_(strict_cast<T>(value)) {}
    150 
    151   // The numeric cast operator basically handles all the magic.
    152   template <typename Dst>
    153   constexpr operator Dst() const {
    154     return strict_cast<Dst>(value_);
    155   }
    156 
    157  private:
    158   const T value_;
    159 };
    160 
    161 // Explicitly make a shorter size_t typedef for convenience.
    162 typedef StrictNumeric<size_t> SizeT;
    163 
    164 }  // namespace base
    165 
    166 #endif  // BASE_NUMERICS_SAFE_CONVERSIONS_H_
    167