c++typesderived-classdowncasttypeid

C++: How to iterate over a list of class types for typeid verification and downcasting?


I would like to perform a down casting at execution time.

For what I read, if I want to do it, I need to compare the typeid of my polymorphic pointer with those of my derived classes, then do the casting in the correct type.

Plus, let's assume that I have a large number of derived classes. This implies I have to write a long switch or list of if.

I would like to reduce this effort by using a list of classes to check.

This could look like:

#include <string.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>

using namespace std;

class BaseShapes
{
        virtual int run() = 0;
};

class ClassRectangle : public BaseShapes
{
        int run()
        {
            std::cout << "I am a Rectangle. " << std::endl;
            return 0;
        }

        float get_length () {return 12.4;};
        float get_width() {return 6.2;};
};

class ClassCircle : public BaseShapes
{
        int run()
        {
            std::cout << "I am a Cricle. " << std::endl;
            return 0;
        }

        float get_diameter() {return 5.3;};
};

float function_only_for_Rectangle(ClassRectangle *rectangle)
{
   // a function coming from a library that I cannot touch and that requires a derived type.
   // But for the example I do something dummy!
   return rectangle->get_length()
};

auto downcast_function (auto *p)
{
    enum ListOfTypes {
        ClassCircle,
        ClassRectangle,
        // and a lot more differents classes
    };

    for ( int fooInt = ClassCircle; fooInt < ClassRectangle; fooInt++ )
    {
        ListOfTypes fooItem = static_cast<ListOfTypes>(fooInt);
        if (typeid(p) == typeid(fooItem))
        {
            auto pCasted =dynamic_cast<fooItem>(p);
            return pCasted;
        }
    }
    
    std::cout<< "downcast_function warning: no typeid is matching !" << std::endl;
    return p;
};

int main(void) 
{ 
    // Beginning of main.
    cout << "(Start)" << endl;
    
        std::unique_ptr<BaseShapes> Shape1(new ClassRectangle());
        auto p=Shape1.get();
        //function_only_for_Rectangle(p); // not working since p is of type BaseShapes*
        auto pbis=downcast_function(p); // should be of type ClassRectangle*
        function_only_for_Rectangle(pbis);
    
    // End of the main.
    cout << "(End) " << endl;   
    return 0; 
} 
// EoF

So how can I write the downcast_function ? Or in other words, how can I iterate over a list of class types in order to make a typeid comparison and a casting ?

More details:

I agree that in this dummy example, I could simply override a function for each derived class and that is a much better way to deal with polymorphism. But I need to downcast, this is a constrain coming from a more complex problem where they are things that I am not allowed to changed. So, the question here is not why downcast but how.

To give a bit more details about my constrains are:

  1. To start from a base pointer.

  2. Get a derived pointer and give it to an external function (here called function_only_for_Rectangle, so I cannot modify this function).

  3. I cannot do a simple and direct dynamic_cast<ClassRectangle>(p) because the type of p (or equivalently Shape1) will change at running time. This means that Shape1 can have "randomly" any derived type from BaseShapes. So I need something "automatic" and this is why I was thinking about iterate over all derived types and downcast according to the typeid match (but I am open to better ideas).

All the classes can modified if needed.


Solution

  • You say "polymorphic" but what you want to do is the opposite of it.

    Instead of trying to work against polymorphism you could actually use it. If all subclasses have their own implementation of a virtual function then the caller does not need to care what the actual dynamic type of the object is. That is runtime polymorphism in a nutshell.

    I suppose the naming for run is only for the example. Give it a better name, supply a default implementation in the base class, implement specific behavior in ClassRectangle and let the caller call it. No need to cast.

    class BaseShapes
    {
            virtual int do_something_rectangly() { return 0;}
            ~virtual BaseShapes() = default;
    };
    
    class ClassRectangle : public BaseShapes
    {
            int do_something_rectangly() override
            {
                std::cout << "I am a Rectangle. " << std::endl;
                return 0;
            }
    };
    
    class ClassCircle : public BaseShapes
    {
        // does not override do_something_rectangly()
    };
    
    int function_for_any_base_shape(BaseShapes& s)
    {
       return s.do_something_rectangly();
    };
    
    int main(void) 
    { 
        // Beginning of main.
        cout << "(Start)" << endl;
        
        std::unique_ptr<BaseShapes> Rec1(new ClassRectangle());
        function_for_any_base_shape(*pbis);
        
        cout << "(End) " << endl;   
        return 0; 
    } 
    

    Concerning your edit:

    1. I cannot do a simple and direct dynamic_cast(p) because the type of p (or equivalently Shape1) will change at running time. This means that Shape1 can have "randomly" any derived type from BaseShapes. [...]

    Either I misunderstand what you wrote completely or you misunderstand how dynamic_cast works. dynamic_cast does already check what the dynamic type of the object is:

    BaseShapes* b1 = new ClassCircle;
    if(ClassRectangle* d = dynamic_cast<ClassRectangle*>(b1))
    {
        // cast is sucessfull
        function_only_for_Rectangle(d);
    } else {
        // dynamic type of b1 is not ClassRectangle
    }
    

    To call function_only_for_Rectangle you do not need to be able to cast to all subtypes of ClassBase. You only need to dynamic_cast to a pointer to ClassRectangle and check if the cast was sucesfull.