c++c++11templatestypesusertype

Differentiate between user type and primitives


I'm trying to differentiate between user types and primitive types in a variadic template.

I have tried overloading binary operator, but that only says that there's no fitting overload for 'user types'...

template <typename T>
void PrintParams(T t)
{
    if (IsAUserType)
        std::cout << typeid(t).name();
    else
                std::cout << t;
}

    template <typename First, typename... Rest>
void PrintParams(First first, Rest... rest)
{
    if (IsAUserType)
        std::cout << typeid(first).name();
    else
                std::cout << first;

    PrintParams(rest...);
}

    // If you know what to do with this, then that would also be very helpful...
    //Overload << operator for user types
//template <typename T>
//friend std::ostream& operator<< (std::ostream& os, T t)
//{
            // 
    //if (std::is_fundamental<t>::value)
        //std::clog << t;
    //else
        //std::clog << typeid(t).name();
//}

expected result for input like (class test, 3.4, "string") would be "test3.4string"


Solution

  • You could split your single argument function up in two and use SFINAE to enable the correct one depending on if the argument is a fundamental type or not:

    template<typename T, typename std::enable_if<std::is_fundamental<T>::value, int>::type = 0>
    void PrintParams(T t) {
        std::cout << t;
    }
    
    template<typename T, typename std::enable_if<!std::is_fundamental<T>::value, int>::type = 0>
    void PrintParams(T t) {
        std::cout << typeid(t).name();
    }
    
    template<typename First, typename... Rest>
    void PrintParams(First first, Rest... rest) {
        PrintParams(first);  // ... and call the single argument version here
        std::cout << ",";
        PrintParams(rest...);
    }
    

    Another way would be to check if the type supports streaming using operator<< instead of checking that it's a fundamental type. That would make streaming work for classes (like std::string and user defined ones too).

    #include <iostream>
    #include <type_traits>
    #include <typeinfo>
    #include <utility>
    
    // SFINAE support
    
    namespace detail {
        template<class>
        struct sfinae_true : std::true_type {};
    
        template<class S, class T>
        static auto test_lshift(int)
            -> sfinae_true<decltype(std::declval<S>() << std::declval<T>())>;
    
        template<class S, class T>
        static auto test_lshift(long) -> std::false_type;
    } // namespace detail
    
    template<class T>
    struct has_ostream : decltype(detail::test_lshift<std::ostream, T>(0)) {};
    
    // using the SFINAE support stuff
    
    template<typename T, typename std::enable_if<has_ostream<T>::value, int>::type = 0>
    void PrintParams(const T& t) {
        std::cout << "Type: " << typeid(t).name() << "\n"
                  << " supports operator<<   Value = " << t << "\n";
    }
    
    template<typename T, typename std::enable_if<!has_ostream<T>::value, int>::type = 0>
    void PrintParams(const T& t) {
        std::cout << "Type: " << typeid(t).name() << "\n"
                  << " does NOT support operator<<\n";
    }
    
    template<typename First, typename... Rest>
    void PrintParams(First first, Rest... rest) {
        PrintParams(first);
        PrintParams(rest...);
    }
    
    // example classes
    
    class Foo { // will not support streaming
        int x = 5;
    };
    
    class Bar { // this should support streaming
        int x = 10;
        friend std::ostream& operator<<(std::ostream&, const Bar&);
    };
    
    std::ostream& operator<<(std::ostream& os, const Bar& b) {
        return os << b.x;
    }
    
    // testing
    
    int main() {
        int i = 2;
        Foo f;
        Bar b;
        std::string s = "Hello world";
    
        PrintParams(i, f, b, s);
    }
    

    Possible output:

    Type: i
     supports operator<<   Value = 2
    Type: 3Foo
     does NOT support operator<<
    Type: 3Bar
     supports operator<<   Value = 10
    Type: NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
     supports operator<<   Value = Hello world