C ++ specialization, type_or or just typeid

I would like to know what is best to use in my situation and why. First of all I heard that using RTTI (typeid) is bad. Can anyone explain why? If I know for sure it's wrong to compare them at runtime? Also, is there an example using boost :: type_of? I didn't find any search through the mighty google :) Another solution for me is specialization, but I would like to specialize in at least 9 types of the new method. Here's an example of what I need:

I have this class

  template<typename A, typename B, typename C>
  class CFoo
  {
     void foo()
     {
       // Some chunk of code depends on old A type
     }

  }

      

So I need to rather check the typeid (which I heard is BAD) and do these 3 implementations in an example like:

 void foo()
   {
      if (typeid(A) == typeid(CSomeClass)
       // Do this chunk of code related to A type
      else
      if (typeid(B) == typeid(CSomeClass)
       // Do this chunk of code related to B type
      else
      if (typeid(C) == typeid(CSomeClass)
       // Do this chunk of code related to C type
   }

      

So what's the best solution? I don't want to specialize in all A, B, C because each type has 3 specializations, so I'll get 9 methods or just this type checking.

+2


source to share


5 answers


This is bad because

  • A, B, and C are known at compile time, but you are using an execution engine. If you call typeid, the compiler will make sure to include the metadata in the object files.
  • If you replace "Make this piece of code related to type A" with actual code that uses the CSomeClass interface, you will see that you cannot compile the code in the case of A! = CSomeClass and A having an incompatible interface. The compiler is still trying to translate the code, even if it never runs. (see example below)

What you usually do is to decompose the code into separate function templates or static member functions of classes, which can be specialized.

Poorly:



template<typename T>
void foo(T x) {
    if (typeid(T)==typeid(int*)) {
        *x = 23; // instantiation error: an int can't be dereferenced
    } else {
        cout << "haha\n";
    }
}
int main() {
    foo(42); // T=int --> instantiation error
}

      

it's better:

template<typename T>
void foo(T x) {
    cout << "haha\n";
}
void foo(int* x) {
    *x = 23;
}
int main() {
    foo(42); // fine, invokes foo<int>(int)
}

      

Cheers, s

+6


source


Well, generally, decisions can occur without RTTI. It may indicate that you have not thought about software development properly. This is bad. Sometimes RTTI can be a good thing.

Third, there is something weird about what you want to do. Could you create an intermediate template designed like this:

template< class T > class TypeWrapper
{
  T t;
public:
  void DoSomething()
  {
  }
};

      

then partly specialized in the following functions:

template<> class TypeWrapper< CSomeClass >
{
  CSomeClass c;
public:
  void DoSomething()
  {
     c.DoThatThing();
  }
};

      



Then in your class define above, you would do something like ...

template

  class CFoo
  {
     TypeWrapper< A > a;
     TypeWrapper< B > b;
     TypeWrapper< C > c;
     void foo()
     {
       a.DoSomething();
       b.DoSomething();
       c.DoSomething();
     }

  }

      

So it only actually does something in the "DoSomething" call if it passes through a partially specialized pattern.

+4


source


The problem lies in the pieces of code that you write for each specialization.

It doesn't matter if you write (in length)

void foo()
{
   if (typeid(A) == typeid(CSomeClass)
    // Do this chunk of code related to A type
   else
   if (typeid(B) == typeid(CSomeClass)
    // Do this chunk of code related to B type
   else
   if (typeid(C) == typeid(CSomeClass)
    // Do this chunk of code related to C type
}

      

or

void foo()
{
   A x;
   foo_( x );
   B y;
   foo_( y );
   C z;
   foo_( z );
}
void foo_( CSomeClass1& ) {}
void foo_( CSomeClass2& ) {}
void foo_( CSomeClass3& ) {}

      

The surface of the second case is that when you add class D, you will be reminded by the compiler that there is an overload for foo_ missing that you must write. This can be forgotten in the first option.

+2


source


I'm afraid this won't work in the first place. These "pieces of code" must be compiled, even if the type is not CSomeClass.

I don't think type_of will help or (if it is the same as auto and decltype in C ++ 0x).

I think you could extract these three chunks into separate functions and overload each one for CSomeClass. (Edit: oh there is else if's

. Then you really need a lot of overloads / specialization. What is this code for?)

Edit2: Your code seems to be hoping to do the equivalent of the following, where int is a special type:

#include <iostream>

template <class T>
bool one() {return false; }

template <>
bool one<int>() { std::cout << "one\n"; return true; }

template <class T>
bool two() {return false; }

template <>
bool two<int>() { std::cout << "two\n"; return true; }

template <class T>
bool three() {return false; }

template <>
bool three<int>() { std::cout << "three\n"; return true; }

template <class A, class B, class C>
struct X
{
    void foo()
    {
        one<A>() || two<B>() || three<C>();
    }
};

int main()
{
    X<int, double, int>().foo(); //one
    X<double, int, int>().foo();  //two
    X<double, double, double>().foo(); //...
    X<double, double, int>().foo(); //three
}

      

+2


source


I think you have your abstractions wrong somewhere.

I would try to redefine A, B, and C in terms of the interfaces they need to expose (abstract base classes in C ++ using virtual methods).

Templating allows mostly duck typing, but it looks like CFoo knows too much about the AB and C classes.

typeid is bad because:

  • typeid can be expensive, bloated binaries, information that shouldn't be required.
  • Not all compilers support it
  • Basically it breaks down the class hierarchy.

I would recommend refactoring: remove the template, define interfaces for A, B, and C instead, and make CFoo for those interfaces. This will force you to refactor the behavior so that A, B, and C are actually targets.

+1


source







All Articles