0
votes

let's assume I have a super polymorphic base class Shape where many other shape classes are derived from it.

now if I have a vector of Shape pointers that contains a pointer to a list of different shape types like this:

vector<Shape*> p;  // p contains pointer to many different shape objects

I know to have access to methods and members of each shape in vector p, I need to use dynamic_cast.

but what if I don't know what vector p actually contains at runtime? how can i safely find the type of an object contained in vector p at runtime?

i also know i can check if casting by dynamic_cast returns NULL or not for success. but does that mean to find the actual type of my shape object in vector p I have to do something like this:

if (dynamic_cast<Circle*> p[i] !=NULL){

// do stuff

}

else if (...) {


}

and repeat this pattern for all other shape types?

but this becomes cumbersome if I have 100 possible shapes. is there any better way to achieve this at rumtime?

ps- consider following scenario:

lets say I need to iterate through the Shape* vector and for example put all the circle objects in a separate vector and vector etc ... now i need to know the actual type of the objects. checking the return if typeid and dynamic_casts for many shapes is not practical.

2
but again I have to check typeid against different shape types. this does not really solve the problem.Arash
dynamic_cast is generally an anti-pattern. Use virtual methods instead. If you need to dynamically dispatch code based on the type of another object, give that object a virtual visit() method and use the visitor pattern instead.cdhowie

2 Answers

2
votes

You can use typeid in typeinfo header.

See for instance this question: How to determine actual object type at runtime in C++;

However, the actual question is "why do you need to know the actual type of your object?": that this is AFAIK not that frequent to need such a functionnality, since polymorphimsm already allows for managing a vast majority of use cases.

I know to have access to methods and members of each shape in vector p, I need to use dynamic_cast.

No, not necessarily! In your case, maybe the following is enough, assuming Shape has an area method, (re)defined in Circle and Rectangle (who both extend the Shape class):

std::vector<Shape*> shapes;
Rectangle rect(...);
Circle circle(...);
shapes.push_back( &rect );
shapes.push_back( &circle );
shapes[0]->area(); // --> calls Rectangle::area()
shapes[1]->area(); // --> calls Circle::area()
0
votes

I came up with solution that I'm not really proud of, but maybe it will be helpfull in creating better one. Key thing I was trying to achieve was to get rid of explicit dynamic_cast and got this one working. There is still a need to name your derieved type twice though. Also, it uses std::function which is told to be slow. C++14 required. I believe there is a way to do it with just smart usage of templates. Or at least get rid of type_switch<A>::cast<B> lanes. Anyway, the code:

#include <iostream>
#include <functional>
#include <typeindex>
#include <unordered_map>

// Basic inheritance cases
struct A
{
    virtual void foo() = 0;
};

struct B : public A
{
    void foo() override { }
    void bfoo() {
        std::cout << "B specific\n";
    }
};

struct C : public A
{
    void foo() override  { }
};

template <typename T>
struct type_switch
{
    using Func = std::function<void(T&)>;
    using Pair = std::pair<std::type_index, Func>;
    using Map = std::unordered_map<std::type_index, Func>;
    Map map;

    type_switch(std::initializer_list<Pair> l) : map(l.begin(),l.end())
    {

    }

    void call(T& a)
    {
        map[typeid(a)](a);
    }

    // allows for "oneliner", without explicit 'call', but it could end in creation of 
    // new type_switch on every loop iteration etc.
    type_switch(T&a, std::initializer_list<Pair> l) : type_switch(l){
        call(a);
    }

    template <typename T2>
    static Func cast(std::function<void(T2&)> f)
    {
        static_assert(std::is_base_of<T, T2>::value, "Invalid cast operation on functors, T2 is not base of T");

        // lot of functor copyings...
        return[f = std::move(f)](T& t) {
            f(static_cast<T2&>(t));
        };
    }
};


int main()
{   
    B b;
    C c;

    int local = 0;

    type_switch<A> sw = { 
            { typeid(B), type_switch<A>::cast<B>( [&local](auto& a) { // auto will deduce into B! No explicit casting
            std::cout << "Handle b, local value is " << local << '\n';
            a.bfoo(); // B specific
            local++; // some outer scode operation
        }) } ,
        { typeid(C), type_switch<A>::cast<C>([&local](auto& a) {  // auto will deduce into C! No explicit casting
            std::cout << "Handle c, local value is " << local << '\n';
            local++; // some outer scode operation
        })
        },
        /*  // this one would trigger static_assert
         { typeid(int), type_switch<A>::cast<int>([&local](auto& a) {  // auto will deduce into C! No explicit casting
            std::cout << "Handle int, local value is " << local << '\n';
            local++; // some outer scode operation
        })
        },*/
    };
    sw.call(b);
    sw.call(c);
    return 0;
}