Click here to Skip to main content
15,867,453 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 30.9K   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 
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.