views:

399

answers:

8

I have a hierarchy of types - GenericClass and a number of derived classes, InterestingDerivedClass included, GenericClass is polymorphic. There's an interface

interface ICallback {
    virtual void DoStuff( GenericClass* ) = 0;
};

which I need to implement. Then I want to detect the case when GenericClass* pointer passed into ICallback::DoStuff() is really a pointer to InterestingDerivedClass:

class CallbackImpl : public ICallback {
    void DoStuff( GenericClass* param ) {
        if( dynamic_cast<InterestingDerivedClass*>( param ) != 0 ) {
            return; //nothing to do here
        }
        //do generic stuff
    }
}

The GenericClass and the derived classes are out of my control, I only control the CallbackImpl.

I timed the dynamic_cast statement - it takes about 1400 cycles which is acceptable for the moment, but looks like not very fast. I tried to read the disassembly of what is executed during dynamic_cast in the debugger and saw it takes a lot of instructions.

Since I really don't need the pointer to the derived class is there a faster way of detecting object type at runtime using RTTI only? Maybe some implementation-specific method that only checks the "is a" relationship but doesn't retrieve the pointer?

+1  A: 

Would comparing type_infos be any faster? (call typeid on parameter param)

James Hopkin
Yes, it's faster but it doesn't test inheritance, only exact matches.
Marcus Lindblom
It may be faster. But its not a good idea (like the original code above).
Martin York
I think he's been lectured sufficiently in the other answers. He asked for a faster way of doing what he's doing. (It's a fair point that comparing type_infos will only catch *exactly* InterestingDerivedClass, not anything derived from it).
James Hopkin
Comparing type_info takes from 600 to 1200 cycles which is formally faster, but unimpressive. Add to this that I had to conduct a whole investigation to find which type the compiler expects for the comparison to succeed and that it only works for exact matches. In the same time dynamic_cast works "out of the box" and checks exactly the "is a" relationship.
sharptooth
+10  A: 

I always look on the use of dynamic_cast as a code smell. You can replace it in all circumstances with polymorphic behaviour and improve your code quality. In your example I would do something like this:

class GenericClass
{
  virtual void DoStuff()
  {
    // do interesting stuff here
  }
};

class InterestingDerivedClass : public GenericClass
{
  void DoStuff()
  {
    // do nothing
  }
};

class CallbackImpl : public ICallback {
    void DoStuff( GenericClass* param ) {
        param->DoStuff();
    }
}

In your case, you cannot modify the target classes, you are programming to a contract implied by the declaration of the GenericClass type. Therefore, there is unlikely to be anything that you can do that would be faster than dynamic_cast would be, since anything else would require modifying the client code.

1800 INFORMATION
+1  A: 

First off, do not optimize prematurely. Second, if you do query an object for a concrete implementation inside, it's likely that there's something wrong with your design (think double dispatch).

As for the original question, introducing a GetRuntimeType() function to a ICallback will do quite nicely: see MFC for on how this can be done.

Anton Gogolev
+5  A: 

As others have said, using a virtual function is good practice. There is another reason for using it, not applicable in your case, as you can't add the VF, but still worth mentioning I think - it may be much faster than using dynamic cast. In some (not very stringent) tests I've done with g++, the virtual function out-performed the dynamic_cast by a factor of 4.

Because there is such a disparity, it may be worth creating your own inheritance hierarchy which wraps the code you don't control. You would create instances of the wrapper using dynamic_cast (once) to decide what derived type to create, and then use virtual functions from there on.

anon
+1 For wrapping
Magnus Skog
+1  A: 

In your concrete use case, the answer is to use virtual functions.

There are situations, however, where you need to downcast dynamically. There are a few techniques to make this operation faster (or much faster depending on how smart your compiler implements dynamic_cast), in particular, if you limit yourself to single inheritance. The main idea is that if you somehow know the exact type, a static_cast is much faster:

f(A* pA)
{
  if (isInstanceOfB(pA))
  {
    B* pB = static_cast<B*>(pA);
    // do B stuff...
  }
}

Of course, the problem is now giving a fast implementation of isInstanceOfB().

See boost::type_traits, for instance.

Tobias
+2  A: 

Looks like a pretty hackish design to me. (As others have mentioned, having to use dynamic_cast is usually a sign that you have a design problem.). But if most of the code is out of your control, there's not much you can do about it, I suppose.

But no, the only general solution I'm aware of is dynamic_Cast. typeid only matches the most derived type, which may work in your case.

On a side note, the reason dynamic_cast is so expensive may be that it has to traverse the entire class hierarchy. If you have a deep hierarchy, that becomes expensive (in other words, don't have a deep hierarchy, in general, but especially in C++).

(Of course the first time you perform the cast, most class descriptor lookups will probably be cache misses, which may have skewed your benchmarking and made it look more expensive than it is)

jalf
A: 

Can you use http://www.boost.org/doc/libs/1_39_0/boost/type_traits/is_convertible.hpp and check the CPU Performance ?

You can also checkout the implementation..

Reference: http://www.boost.org/doc/libs/1_39_0/libs/type_traits/doc/html/boost_typetraits/reference/is_convertible.html

Warrior
+1  A: 

Standard dynamic_cast is very flexible, but usually very slow, as it handles many corners cases you are probably not interested about. If you use single inheritances, you can replace it with a simple implementation based on virtual functions.

Example implementation:

// fast dynamic cast
//! Fast dynamic cast declaration
/*!
Place USE_CASTING to class that should be recnognized by dynamic casting.
Do not forget do use DEFINE_CASTING near class definition.
*\note Function dyn_cast is fast and robust when used correctly.
Each class that should be used as target for dyn_cast
must use USE_CASTING and DEFINE_CASTING macros.\n
Forgetting to do so may lead to incorrect program execution,
because class may be sharing _classId with its parent and IsClassId
will return true for both parent and derived class, making impossible'
to distinguish between them.
*/
#define USE_CASTING(baseType) \
  public: \
  static int _classId; \
  virtual size_t dyn_sizeof() const {return sizeof(*this);} \
  bool IsClassId( const int *t ) const \
  { \
    if( &_classId==t ) return true; \
    return baseType::IsClassId(t); \
  }

//! Fast dynamic cast root declaration
/*!
Place USE_CASTING_ROOT to class that should act as
root of dynamic casting hierarchy
*/

#define USE_CASTING_ROOT \
  public: \
  static int _classId; \
  virtual size_t dyn_sizeof() const {return sizeof(*this);} \
  virtual bool IsClassId( const int *t ) const { return ( &_classId==t ); }

//! Fast dynamic cast definition
#define DEFINE_CASTING(Type) \
  int Type::_classId;

template <class To,class From>
To *dyn_cast( From *from )
{
  if( !from ) return NULL;
  if( from->IsClassId(&To::_classId) )
  {
    assert(dynamic_cast<To *>(from));
    return static_cast<To *>(from);
  }
  return NULL;
}

That said, I complete agree with others dynamic_cast is suspicious and you will most often be able to achieve the same goal in a lot cleaner way. That said, similiar to goto, there may be some cases where it can be really useful and more readable.

Another note: if you say the classes in question are out of your control, this solution will not help you, as it requires you to modify the classes (not much, just just add a few lines, but you need to modify them). If this is really the case, you need to use what the language offers, that is dynamic_cast and typeinfo.

Suma