deW1
deW1

Reputation: 5660

How can I find out what type the template<typename type> is?

Let's say I have this code for example:

#include <iostream>

template< typename typeOne , typename typeTwo >
void doWhatever( typeOne a , typeTwo b )
{
    if( typeOne == std::string )
    {
        std::cout << "Hey I found a string" << std::endl;
    }
    else if( typeOne == int )
    {
        std::cout << "Oh shoot! Now it's an integer!" << std::endl;
    }
    else
    {
        std::cout << "Are you mad?! What is that?" << std::endl;
    }
    // if a is string for example cast it to int
}

int main()
{
    doWhatever( "123" , 10 );

    return 0;
}

This obviously doesn't work.

How can I check the type of the typename within my function?

Upvotes: 2

Views: 158

Answers (4)

aaragon
aaragon

Reputation: 2402

Why don't you just use the name() function from typeid? The function print_type below prints the mangled symbol, but you can demangle it if you want:

#include <cstdlib>
#include <typeinfo>
#include <vector>
#include <string>
#include <iostream>

using std::cout;
using std::endl;


template <class T>
void cppfilt(const T& t) {
  const std::type_info& ti = typeid(t);
  static const std::string command("c++filt -t ");
  system((command + std::string(ti.name())).c_str());
}

template <typename T>
void print_type(T& t) {
 cout<<"Type: "<<typeid(t).name()<<endl;
}

int main(){

  std::string s("hello world!");
  int v = 5;

  cout<<"Pring mangled ty:pes"<<endl;
  print_type(s);
  print_type(v);


  cout<<"\nDemangle symbols using c++filt"<<endl;
  cppfilt(s);
  cppfilt(v);

  return 0;
}

This code outputs:

Pring mangled ty:pes
Type: Ss
Type: i

Demangle symbols using c++filt
std::basic_string<char, std::char_traits<char>, std::allocator<char> >
int

You can find the code here. And it works for any type.

Upvotes: 0

odinthenerd
odinthenerd

Reputation: 5562

Sadly you cannot partially specialize a function template. This leaves us basically four ways to do this:

  1. Regular function overloading
  2. Tag dispatch
  3. SFINAE
  4. Class template specialization

Regular function overloading would do the trick for your example code but might not cut it for real code that many be more complicated. The idea is to make two function templates with the desired parameter type as a normal parameter:

template< typename typeTwo >
void doWhatever( std::string a , typeTwo b )
{
    std::cout << "Hey I found a string" << std::endl;
}
template< typename typeTwo >
void doWhatever( int a , typeTwo b )
{
    std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}
template< typename typeOne, typename typeTwo >
void doWhatever( typeOne a , typeTwo b )
{
    std::cout << "Are you mad?! What is that?" << std::endl;
}

Tag dispatch is probably the best way to go. The idea is to make a metafunction (i.e. class template with a nested type alias names "Type") and overload based on the type of that tag:

struct IntTag{};
struct StringTag{};
struct BadTag{};

template<typename T>
struct MakeTag{
    using Type = BadTag;
}
template<>
struct MakeTag<int>{
    using Type = IntTag;
}

template<>
struct MakeTag<std::string>{
    using Type = StringTag;
}
template< typename typeOne , typename typeTwo >
void doHelper( IntTag, typeOne a , typeTwo b ) {
    std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}

template< typename typeOne , typename typeTwo >
void doHelper( StringTag, typeOne a , typeTwo b ) {
    std::cout << "Hey I found a string" << std::endl;
}

template< typename typeOne , typename typeTwo >
void doHelper( BadTag, typeOne a , typeTwo b ) {
    std::cout << "Are you mad?! What is that?" << std::endl;
}

template< typename typeOne , typename typeTwo >
void doWhatever( typeOne a , typeTwo b ) {
    doHelper(typename MakeTag<typeOne>::Type{},a,b);
}

the advantage to this is that the user can specialize MakeTag for their type which is not an int but convertable to one for example. It is also possible to select catagories of input for one single tag.

SFINAE is the language feature behind std::enable_if, the idea is that if a condition is true then std::enable_if has a nested type, if not it does not which causes the function to be removed from the set of candidates.

template< typename typeOne , typename typeTwo, typename = std::enable_if_t<std::is_same<typeOne,std::string>::value> >
void doWhatever( typeOne a , typeTwo b )
{
    std::cout << "Hey I found a string" << std::endl;
}
template< typename typeOne , typename typeTwo, typename = std::enable_if_t<std::is_same<typeOne,int>::value> >
void doWhatever( typeOne a , typeTwo b )
{
    std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}
template< typename typeOne , typename typeTwo, typename = std::enable_if_t<!(std::is_same<typeOne,std::string>::value || std::is_same<typeOne,int>::value)> >
void doWhatever( typeOne a , typeTwo b )
{
    std::cout << "Are you mad?! What is that?" << std::endl;
}

Class template specialization is well described in Andrews answer.

Upvotes: 0

John Dibling
John Dibling

Reputation: 101506

Instead of having one instantiation of a function template that handles all types, you could have one instantiation for each type you wish to handle. This is accomplished via either function template specialization, class template specialization, or class template partial specialization. You could also -- and often should -- not use templates at all and simply provided overloaded non-template function overloads. Here's an example of function template specialization:

#include <cstdlib>
#include <string>
#include <iostream>

template< typename typeOne >
void doWhatever( typeOne a);

template <>
void doWhatever <std::string> (std::string a)
{
   std::cout << "Hey I found a string" << std::endl;
}

template <> void doWhatever <const char *> (const char* a)
{
  std::cout << "A C-string\n";
}

template <>
void doWhatever <int> (int a)
{
  std::cout << "Oh shoot! Now it's an integer!" << std::endl;
}

template <typename typeOne>
void doWhatever (typeOne a)
{
  std::cout << "Are you mad?! What is that?" << std::endl;
}

int main()
{
    doWhatever( "123" );

    return 0;
}

There are a few things to note here.

  • When using templates, it's often better if you don't need to disambiguate the type of a template parameter. If you're using templates and you do need to know the types, maybe you shouldn't be using a template here. A better approach might be to simply provide overloads of a non-template function for the types you do wish to support.

  • The literal "123" isn't interpreted as a std::string, but a const char*. I provided this specialization above:

    template <> void doWhatever (const char* a) { std::cout << "A C-string\n"; }

  • I've eliminated the second, unused template parameter in my implementation above. That is because partial function template specialization is not supported on C++. This fact yields a few other things to note.

  • If you really do wish to use partial specialization (perhaps because you really need to use templates but only disambiguate one or some of the parameters), then you can use class template partial specialization.

  • The function template specialization I've provided here also implements an unspecialized function template instantiation. This acts as a catch-all for any type you haven't specifically mentioned. Eliminating that catch-all will result in a compiler error for any unsupported type. In many cases, this is a good thing.

  • If I found myself writing the code above in a real system, I'd probably eliminate it and use simple non-template function overloads instead. Function template specialization is an awfully fancy hammer for such a simple task.

Upvotes: 1

You can use std::is_same for this:

#include <type_traits>

template< typename typeOne , typename typeTwo >
void doWhatever( typeOne a , typeTwo b )
{
    if( std::is_same<typeOne, std::string>::value )
    {
        std::cout << "Hey I found a string" << std::endl;
    }
    else if( std::is_same<typeOne, int>::value )
    {
        std::cout << "Oh shoot! Now it's an integer!" << std::endl;
    }
    else
    {
        std::cout << "Are you mad?! What is that?" << std::endl;
    }
    // if a is string for example cast it to int
}

Note that even the inactive branches are still processed by the compiler and must thus be syntactically and semantically valid (even though they will never be executed, so they can contain runtime errors).

If that is a problem for you (i.e. you need to only compile the relevant part), you'll have to use a "delegate to class" trick:

template <typename typeOne, typename typeTwo>
void doWhatever(typeOne a, typeTwo b)
{
  doWhatever_helper<typeOne, typeTwo>::do_specific_part(a, b);
  do_other_parts();
}


template <typename typeOne, typename typeTwo>
struct doWhatever_helper
{
  static void do_specific_part(typeOne a, typeTwo b) {
    std::cout << "Are you mad?! What is that?" << std::endl;
  }
};

template <typename typeTwo>
struct doWhatever_helper<std::string, typeTwo>
{
  static void do_specific_part(std::string a, typeTwo b) {
    std::cout << "Hey I found a string" << std::endl;
  }
};

template <typename typeTwo>
struct doWhatever_helper<int, typeTwo>
{
  static void do_specific_part(int a, typeTwo b) {
    std::cout << "Oh shoot! Now it's an integer!" << std::endl;
  }
};

Upvotes: 6

Related Questions