Differentiate between user type and primitives

364 views Asked by At

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"

3

There are 3 answers

3
Ted Lyngmo On BEST ANSWER

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
3
selalerer On

I think std::is_class can replace your IsAUserType.

https://en.cppreference.com/w/cpp/types/is_class

1
Safyan Yaqoob On

Primitive data types include integer , character , void , float etc..which are defined already inside the language i.e , user can use these data types without defining them inside the language. User defined data types are the data types which user have to define while or before using them.