Friday, April 8, 2011

Identifying primitive types in templates

Hello

I am looking for a way to identify primitives types in a template class definition.

I mean, having this class :

template<class T>
class A{
void doWork(){
   if(T isPrimitiveType())
     doSomething();
   else
     doSomethingElse(); 
}
private:
T *t; 
};

Is there is any way to "implement" isPrimitiveType().

From stackoverflow
  • // Generic: Not primitive
    template<class T>
    bool isPrimitiveType() {
        return false;
    }
    
    // Now, you have to create specializations for **all** primitive types
    
    template<>
    bool isPrimitiveType<int>() {
        return true;
    }
    
    // TODO: bool, double, char, ....
    
    // Usage:
    template<class T>
    void test() {
        if (isPrimitiveType<T>()) {
            std::cout << "Primitive" << std::endl;
        } else {
            std::cout << "Not primitive" << std::endl;
        }
     }
    

    In order to save the function call overhead, use structs:

    template<class T>
    struct IsPrimitiveType {
        enum { VALUE = 0 };
    };
    
    template<>
    struct IsPrimitiveType<int> {
        enum { VALUE = 1 };
    };
    
    // ...
    
    template<class T>
    void test() {
        if (IsPrimitiveType<T>::VALUE) {
            // ...
        } else {
            // ...
        }
    }
    

    As others have pointed out, you can save your time implementing that by yourself and use is_fundamental from the Boost Type Traits Library, which seems to do exactly the same.

    Ben : Yes, but what I want is specilize my class *for all* primitives types :/
    Ferdinand Beyer : See my edited answer.
  • Boost TypeTraits has plenty of stuff.

  • Assuming by 'Primitive Type' you mean the built in types you can do a series of template specializations. Your code would become:

    template<class T>
    struct A{
        void doWork();
    private:
        T *t; 
    };
    
    template<> void A<float>::doWork()
    {
        doSomething();
    }
    
    template<> void A<int>::doWork()
    {
        doSomething();
    }
    
    // etc. for whatever types you like
    
    template<class T> void A<T>::doWork()
    {
        doSomethingElse();
    }
    
  • The following example (first posted in comp.lang.c++.moderated) illustrates using partial specialisation to print things differently depending on whether or not they are built-in types.

    // some template stuff
    //--------------------
    #include <iostream>
    #include <vector>
    #include <list>
    
    using namespace std;
    
    // test for numeric types
    //-------------------------
    template <typename T> struct IsNum {
        enum { Yes = 0, No = 1 };
    };
    
    
    template <> struct IsNum <int> { 
        enum { Yes = 1, No = 0 };
    };
    
    
    template <> struct IsNum <double> {
        enum { Yes = 1, No = 0 };
    };
    
    // add more IsNum types as required
    
    // template with specialisation for collections and numeric types
    //---------------------------------------------------------------
    template <typename T, bool num = false> struct Printer {
        void Print( const T & t ) {
            typename T::const_iterator it = t.begin();
            while( it != t.end() ) {
                cout << *it << " ";
                ++it;
            }
            cout << endl;
        }
    };
    
    template <typename T> struct Printer <T, true> {
        void Print( const T & t ) {
            cout << t << endl;
        }
    };
    
    // print function instantiates printer depoending on whether or
    // not we are trying to print numeric type
    //-------------------------------------------------------------
    template <class T> void MyPrint( const T & t ) {
        Printer <T, IsNum<T>::Yes> p;
        p.Print( t );
    }
    
    // some test types
    //----------------
    typedef std::vector <int> Vec;
    typedef std::list <int> List;
    
    // test it all
    //------------
    int main() {
    
        Vec x;
        x.push_back( 1 );
        x.push_back( 2 );
        MyPrint( x );        // prints 1 2
    
        List y;
        y.push_back( 3 );
        y.push_back( 4 );
        MyPrint( y );        // prints 3 4
    
        int z = 42;
        MyPrint( z );        // prints 42
    
        return 0;
    }
    
    Ben : this helped, thanks !
  • It cannot be done exactly the way you asked. Here is how it can be done :

    template<class T>
    class A{
    void doWork(){
       bool isPrimitive = boost::is_fundamental<T>::value;
       if(isPrimitive)
         doSomething();
       else
         doSomethingElse(); 
    }
    private:
    T *t; 
    };
    

    You may get a warning if you put the value of isPrimitive directly inside the if statement. This is why i introduced a temporary variable.

  • Yet another similar examples:

    #include  <boost/type_traits/is_fundamental.hpp>
    #include <iostream>
    
    template<typename T, bool=true>
    struct foo_impl
    {
        void do_work()
        {
            std::cout << "0" << std::endl;
        }
    };
    template<typename T>
    struct foo_impl<T,false>
    {
        void do_work()
        {
            std::cout << "1" << std::endl;
        }
    };
    
    template<class T>
    struct foo
    {
        void do_work()
        {
            foo_impl<T, boost::is_fundamental<T>::value>().do_work();
        }
    };
    
    
    int main()
    {
        foo<int> a; a.do_work();
        foo<std::string> b; b.do_work();
    }
    
    BenoƮt : Your version is a bit more efficient that mine, but i considered that it did not compensate for the readability cost.
    Anonymous : Probably there are even nicer ways of doing this. The above was quickly hacked in vim to test the idea...

0 comments:

Post a Comment