Click here to Skip to main content
15,883,922 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 
If you call Query interface on a const object, then it should return a pointer to a constant object. Thus, it create an hole in safety as it make it possible to modify const objects without even using const_cast. If you code in C++, then you should follow the philosophy of that langage.

One of the reason that your implementation is faster is because it does not handle multiple inheritance. And it might not follow same rules as dynamic_cast if for example your base is private...

By the way, if you have a performance problem with dynamic_cast, then your code is surely very poorly designed as in properly class, one seldom need dynamic_cast at all as he will use virtual functions to handle things that are to be handled differently by derived classes. In some case, one might also used visitor patterns as needed to avoid too much functionality in those classes.

Finally, using raw pointers is a discutable practice. With modern C++, one would want to avoid those to help ensure correct applications.

Thus, even though it might be an interesting article, I would not recommand that approach in production code. On large application, it might be harder to ensure quality code as it uses raw pointers and does not properly respect constness.
Philippe Mori

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.