Using boost::numeric_cast<>

后端 未结 3 383
盖世英雄少女心
盖世英雄少女心 2021-01-08 00:11

When I want to convert between different integer types, it seems the best syntax is to use boost::numeric_cast<>():

int y = 99999;
short x         


        
相关标签:
3条回答
  • 2021-01-08 00:38

    Hm... If the above works, a general solution would probably be to make something like:

    template<typename TypeFrom, typename TypeTo>
    TypeTo saturated_cast(TypeFrom value) {
        TypeTo valueMin = std::numeric_limits<TypeTo>::min();
        TypeTo valueMax = std::numeric_limits<TypeTo>::max();
        return boost::numeric_cast<TypeTo>( std::max(std::min(value,valueMax),valueMin) );
    }
    

    Hope I got it right... Anyway, you've got the concept :)

    .... BTW: I think you could use static_cast here instead because after performing the limitation you cannot overflow the range any more, so you don't need additional checking of numeric_cast.

    0 讨论(0)
  • 2021-01-08 00:43

    If you're okay with C++17 but don't want your casting algorithm throwing exceptions internally, you can use std::clamp with a bit of wrapping to handle out-of-bounds values.

    template <typename TTo, typename TFrom>
    constexpr TTo clamp_cast(const TFrom& src) noexcept
    {
        using to_limits   = std::numeric_limits<TTo>;
        using larger_type = std::conditional_t<(sizeof(TFrom) < sizeof(TTo)), TTo, TFrom>;
    
        if constexpr (std::is_same_v<TTo, TFrom>)
        {
            // don't bother if it is the same type
            return src;
        }
        else if constexpr (std::is_unsigned_v<TFrom>)
        {
            // if source is unsigned, we only need to worry about upper bound
            return TTo(std::min(larger_type(src), larger_type(to_limits::max())));
        }
        else if constexpr (std::is_unsigned_v<TTo>)
        {
            // source is signed, but destination is not
            if (src < TFrom(0))
                return TTo(0);
            else
                return TTo(std::min(larger_type(src), larger_type(to_limits::max())));
        }
        else
        {
            // both are signed -- use regular clamping rules
            return TTo(std::clamp(larger_type(src),
                                  larger_type(to_limits::min()),
                                  larger_type(to_limits::max())
                                 )
                      );
        }
    }
    

    Usage is basically what you'd expect:

    static_assert(uint16_t(213)   == clamp_cast<uint16_t>(213));
    static_assert(uint16_t(65535) == clamp_cast<uint16_t>(9872431));
    static_assert(uint16_t(0)     == clamp_cast<uint16_t>(-98721));
    
    0 讨论(0)
  • 2021-01-08 00:46

    You could probably do something like this:

    #include <limits>
    
    template<typename Target, typename Source>
    Target saturation_cast(Source src) {
       try {
          return boost::numeric_cast<Target>(src);
       }
       catch (const boost::negative_overflow &e) {
          return std::numeric_limits<Target>::lowest();
          /* Or, before C++11:
          if (std::numeric_limits<Target>::is_integer)
             return std::numeric_limits<Target>::min();
          else
             return -std::numeric_limits<Target>::max();
          */
       }
       catch (const boost::positive_overflow &e) {
          return std::numeric_limits<Target>::max();
       }
    }
    

    (For types that support it the error cases could also return -inf/+inf).

    This way you let Boost's numeric_cast determine if the value is out of bounds and can then react accordingly.

    0 讨论(0)
提交回复
热议问题