Click here to Skip to main content
15,887,135 members
Articles / Programming Languages / C++
Tip/Trick

C++: Custom RTTI

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
12 Apr 2016CPOL1 min read 31.1K   314   12   13
This tip presents a custom RTTI class which is 10x faster than dynamic_cast

This tip presents a intrusive custom RTTI class which provides better performance than dynamic_cast. This class is found in the Real-Time 3D Rendering with DirectX and HLSL by Paul Varcholik. Benchmark shows 10x performance lead over dynamic_cast. For those who may not be familiar, dynamic_cast is used in C++ to do downcast to derived class. For upcast to base class generally do not need any explicit casting. Runtime Type Information (RTTI) must be enabled in the compiler to use dynamic_cast.

Benchmark under VC2010 Windows
     DynamicCast: 1266ms
          AsCast:  127ms

Below shows the entire RTTI class which other class needs to be inherited from. User has to define RTTI_DECLARATIONS macro inside their class.

C++
#pragma once

#include <string>

namespace Library
{
    class RTTI
    {
    public:
        virtual const size_t TypeIdInstance() const = 0;
        
        virtual RTTI* QueryInterface(const size_t)
        {
            return nullptr;
        }
        virtual const RTTI* QueryInterface(const size_t) const
        {
            return nullptr;
        }

        virtual bool Is(const size_t id) const
        {
            return false;
        }

        virtual bool Is(const std::string& name) const
        {
            return false;
        }

        template <typename T>
        T* As() 
        {
            if (Is(T::TypeIdClass()))
            {
                return (T*)this;
            }

            return nullptr;
        }
        template <typename T>
        const T* As() const
        {
            if (Is(T::TypeIdClass()))
            {
                return (T*)this;
            }

            return nullptr;
        }
    };

#define RTTI_DECLARATIONS(Type, ParentType)                            \
    public:                                                            \
        static std::string TypeName() { return std::string(#Type); }   \
        virtual const size_t TypeIdInstance() const                    \
        { return Type::TypeIdClass(); }                                \
        static const size_t TypeIdClass()                              \
        { static int d = 0; return (size_t) &d; }                      \
        virtual Library::RTTI* QueryInterface( const size_t id )       \
        {                                                              \
            if (id == TypeIdClass())                                   \
                { return (RTTI*)this; }                                \
            else                                                       \
                { return ParentType::QueryInterface(id); }             \
        }                                                              \
        virtual const Library::RTTI* QueryInterface( const size_t id ) const \
        {                                                              \
            if (id == TypeIdClass())                                   \
                { return (RTTI*)this; }                                \
            else                                                       \
                { return ParentType::QueryInterface(id); }             \
        }                                                              \
        virtual bool Is(const size_t id) const                         \
        {                                                              \
            if (id == TypeIdClass())                                   \
                { return true; }                                       \
            else                                                       \
                { return ParentType::Is(id); }                         \
        }                                                              \
        virtual bool Is(const std::string& name) const                 \
        {                                                              \
            if (name == TypeName())                                    \
                { return true; }                                       \
            else                                                       \
                { return ParentType::Is(name); }                       \
        }                                                              
}

The trick to uniquely identify a class is by using address of a local static member which should be unique. Credit: Andrew Fedoniouk aka c-smile.

C++
static const size_t TypeIdClass()
{ static int d = 0; return (size_t) &d; }

The usage difference between dynamic_cast and As() member is shown below

C++
Parallelogram* p = dynamic_cast<Parallelogram*>(vec[i]);

Parallelogram* p = vec[i]->As<Parallelogram>();

As the reader may notice As() function do C style cast instead of reinterpret_cast. The compiler will complain of using reinterpret_cast in a const member function.

C++
template <typename T>
const T* As() const
{
    if (Is(T::TypeIdClass()))
    {
        return (T*)this;
    }

    return nullptr;
}

For those who want to use this class in C++98, just change nullptr to NULL. Note: this class does not work in multiple inheritance if more than 1 base class derive from it. Source code is hosted at Github.

History

  • 2015-12-31: Initial release
  • 2016-01-02: Replaced sRunTimeTypeId with a static local variable, so the class is "header only" and Makefile is fixed.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Singapore Singapore
Shao Voon is from Singapore. His interest lies primarily in computer graphics, software optimization, concurrency, security, and Agile methodologies.

In recent years, he shifted focus to software safety research. His hobby is writing a free C++ DirectX photo slideshow application which can be viewed here.

Comments and Discussions

 
QuestionRTTI_DEFINITIONS(Type) is redundant Pin
c-smile1-Jan-16 18:58
c-smile1-Jan-16 18:58 
AnswerRe: RTTI_DEFINITIONS(Type) is redundant Pin
Shao Voon Wong1-Jan-16 19:32
mvaShao Voon Wong1-Jan-16 19:32 
GeneralRe: RTTI_DEFINITIONS(Type) is redundant Pin
Shao Voon Wong3-Jan-16 2:50
mvaShao Voon Wong3-Jan-16 2:50 
QuestionConstness does not seems to be respected Pin
Philippe Mori31-Dec-15 6:33
Philippe Mori31-Dec-15 6:33 
AnswerRe: Constness does not seems to be respected Pin
Shao Voon Wong1-Jan-16 17:21
mvaShao Voon Wong1-Jan-16 17:21 
GeneralRe: Constness does not seems to be respected Pin
Philippe Mori2-Jan-16 4:07
Philippe Mori2-Jan-16 4:07 
If you can modify the class, then why not implement the visitor design patterns. It would be faster as it will be only 2 virtual function calls.

Visitor Design Pattern in C++: Before and after[^]

You can also add support for const objects by having 2 accept functions and 2 classes hierarchies.

The main drawback of that pattern is that if your hierarchies is not stable, then you will have a lot of maintenance to do...

I don't pretend that multiple inheritance is good... It is almost preferable to use composition. However, your code can easily be incorrectly used. For one thing, by using C style cast, you loose the benefit of static_cast...

And for raw pointers, it is not clear who owns it at first sight... This is even worst as a function like QueryInterface give the ownership to the caller in traditionnal COM (Active/X) so an user of your code might assume that if he come from such background.
Philippe Mori

Suggestionnote for multiple inheritance Pin
D4rkTrick4-Jan-16 10:13
professionalD4rkTrick4-Jan-16 10:13 
GeneralRe: note for multiple inheritance Pin
Shao Voon Wong4-Jan-16 17:47
mvaShao Voon Wong4-Jan-16 17:47 
GeneralRe: Constness does not seems to be respected Pin
William E. Kempf4-Jan-16 10:43
William E. Kempf4-Jan-16 10:43 
GeneralRe: Constness does not seems to be respected Pin
Shao Voon Wong4-Jan-16 13:43
mvaShao Voon Wong4-Jan-16 13:43 
GeneralRe: Constness does not seems to be respected Pin
William E. Kempf5-Jan-16 2:35
William E. Kempf5-Jan-16 2:35 
GeneralRe: Constness does not seems to be respected Pin
Stefan_Lang22-Apr-16 5:40
Stefan_Lang22-Apr-16 5:40 
GeneralRe: Constness does not seems to be respected Pin
Shao Voon Wong22-Apr-16 15:25
mvaShao Voon Wong22-Apr-16 15:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.