Search code examples
c++typeserror-handlingtype-conversionnumeric-limits

How to pass values greater than std::numeric_limits<fundamental_type>::max() to a function?


I am writing a function void func(K const& val), which part of a template class.

Assume I define a class object as foo<unsigned short int> f; and then I call func as f.func(999999);

What happens is the value 999999gets converted to some other value as it itself is out of range. How do I prevent this from happening? Is there a compiler flag or some other way in code where I can prevent this?

Checking

if(val > std::numeric_limits<K>::max())

inside the func does not help, as the value is already converted when it is getting passed and into something that is inside the numeric limit.

For example, 999998989898988989999 gets converted to 11823

How do I pass values greater than std::numeric_limits::max() to a function?

P.S. I am compiling using gcc (with -std=c++11)


Solution

  • Assuming the definition of your class foo is something like:

    template<typename K>
    class foo {
    public:
        void func(K const& k) {}
    };
    

    You could make func template itself, and check for limits before calling the actual implementation:

    #include <iostream>
    #include <limits>
    
    template<typename K>
    class foo {
    public:
        template<typename T>
        std::enable_if_t<std::is_signed<T>::value && !std::is_signed<K>::value> func(T const& t) 
        {
            if (t < 0 || static_cast<std::make_unsigned_t<T>>(t) > std::numeric_limits<K>::max()) {
                std::cout << "error\n";
                return;
            }
            
            func_impl(t);
        }
        
        template<typename T>
        std::enable_if_t<!std::is_signed<T>::value && std::is_signed<K>::value> func(T const& t) 
        {
            if (t > static_cast<std::make_unsigned_t<K>>(std::numeric_limits<K>::max())) {
                std::cout << "error\n";
                return;
            }
            
            func_impl(t);
        }
        
        template<typename T>
        std::enable_if_t<std::is_signed<T>::value == std::is_signed<K>::value> func(T const& k)
        {
            if (k < std::numeric_limits<K>::min() || k > std::numeric_limits<K>::max()) {
                std::cout << "error\n";
                return;
            }
            func_impl(k);
        }
        
    private:
        void func_impl(K const& k) 
        {
            std::cout << "normal\n";
        }
    };
    
    
    int main()
    {
        foo<char>().func(127);
        foo<char>().func(127u);
        foo<char>().func(-128);
        foo<char>().func(128);
        foo<char>().func(128u);
        foo<char>().func(-129);
        foo<unsigned>().func(-1);
        foo<int>().func(1u);
    }
    

    Output:

    normal

    normal

    normal

    error

    error

    error

    error

    normal

    LIVE

    Edit

    As @BaumMitAugen pointed out, boost::numeric_cast would probably be a better alternative to manual ifs in my implementation of func - it will throw an exception if an underflow or overflow occurs and you will avoid a lot of boilerplate of the latest edited version of code which (a) works and (b) avoids signed/unsigned comparison warnings.