views:

240

answers:

3

I wanted to check that typeid is evaluated at compile time when used with a type name (ie typeid(int), typeid(std::string)...).

To do so, I repeated in a loop the comparison of two typeid calls, and compiled it with optimizations enabled, in order to see if the compiler simplified the loop (by looking at the execution time which is 1us when it simplifies instead of 160ms when it does not).

And I get strange results, because sometimes the compiler simplifies the code, and sometimes it does not. I use g++ (I tried different 4.x versions), and here is the program:

#include <iostream>
#include <typeinfo>
#include <time.h>

class DisplayData {};

class RobotDisplay: public DisplayData {};
class SensorDisplay: public DisplayData {};

class RobotQt {};
class SensorQt {};

timespec tp1, tp2;
const int n = 1000000000;

int main()
{
    int avg = 0;
    clock_gettime(CLOCK_REALTIME, &tp1);
    for(int i = 0; i < n; ++i)
    {
//      if (typeid(RobotQt) == typeid(RobotDisplay))    // (1) compile time
//      if (typeid(SensorQt) == typeid(SensorDisplay))  // (2) compile time
        if (typeid(RobotQt) == typeid(RobotDisplay) || 
            typeid(SensorQt) == typeid(SensorDisplay))    // (3) not compile time ???!!!
            avg++;
        else
            avg--;
    }
    clock_gettime(CLOCK_REALTIME, &tp2);
    std::cout << "time (" << avg << "): " << 
        (tp2.tv_sec-tp1.tv_sec)*1000000000+(tp2.tv_nsec-tp1.tv_nsec) << 
        " ns" << std::endl;
}

The conditions in which this problem appear are not clear, but:
- if there is no inheritance involved, no problem (always compile time)
- if I do only one comparison, no problem
- the problem only appears only with a disjunction of comparisons if all the terms are false

So is there something I didn't get with how typeid works (is it always supposed to be evaluated at compilation time when used with type names?) or may this be a gcc bug in evaluation or optimization?

About the context, I tracked down the problem to this very simplified example, but my goal is to use typeid with template types (as partial function template specialization is not possible).

Thanks for your help!

+3  A: 

I don't really know the answer to your question but if you use is_same<> metafunction instead of typeid you might get more desirable results. Even if you don't have access to this metafunction, it is very easy to write one:


template < typename T1, typename T2 >
struct is_same
{
  enum { value = false }; // is_same represents a bool.
  typedef is_same<T1,T2> type; // to qualify as a metafunction.
};

template < typename T >
struct is_same
{
  enum { value = true };
  typedef is_same<T,T> type;
};
Noah Roberts
Wonderful!However I had to modify a little bit the function in order to work:[ template < typename T > struct is_same<T,T> ] [ if (is_same<Type1,Type2>::value) {} ]And there is a boost::is_same.Thanks a lot, I will do that!
cyril42e
+2  A: 

typeid is part of the Run-Time Type Identification mechanism, which suggests what it's useful for: it's main usage is identifying the dynamic type of a pointer/reference to a base class at runtime. When the types are statically known at compile-time, you don't need to "identify" them as you already know what they are.

In the example, there is nothing to identify at runtime, though, yet the results are not in any way useful at compile-time (typeid cannot appear in const-expressions, which is what you need for template metaprogramming).

Therefore I also recommend is_same

UncleBens
Yes I agree, I didn't know about is_same, but this is indeed what I want. Thanks!
cyril42e
A: 

For any type T, if T is polymorphic, the compiler is required to evaluate the typeid stuff at runtime. If T is non-polymorphic, the compiler is required to evaluate the typeid stuff at compile time. However, i cannot find the relevant reference in the C++ draft (n3000.pdf) for it.

Infact, in one of the projects that i worked on, this trick was used to find whether a class was polymorphic at runtime.

template <class T>  
bool isPolymorphic() {  
    bool answer=false; 
    T *t = new T(); 
    typeid(answer=true,*t);  
    delete t; 
    return answer;  
} 

I had asked a related question here on SO a few months back.

Abhay
Ok but you are using typeid on an object, not a type name. Even if the type is polymorphic, given the type name it is completely known at compile time, isn't it?
cyril42e
What was the point of doing `new`/`delete` instead of just declaring a local object?
AndreyT
@AndreyT: Anything that yields `lvalue` would do for the typeid operator. I just put the answer from the other thread for completeness.
Abhay
@cyril42e: I tried to answer to your question as to when is typeid operator evaluated. The example i gave just uses the evaluation semantics to do something useful.
Abhay
Factually untrue, but close. The expression must be evaluated and the correct type_info must be returned. But in the code above, the compiler is in fact allowed to eliminate the entire runtime typeid check. As long as the `answer=true` part remains, the observable behavior doesn't change.
MSalters