Michael
Michael

Reputation: 459

How to get real class instance name from a vector?

I have class:

class IComponent{};

And I use it like base class for other specific classes.

class First : public IComponent{public: void First();};   
class Second : public IComponent{public: void Second();};

And I have a class Object that have a vector that stores classes inherited from IComponent.

vector<IComponent*> vectorOfComponents;

In this class i have a template method that adds component to vector.

template<typename comp>
void AddComponent() {
    comp * c = new comp();
    getComponents().push_back(c);
}

But I want also to have possibility to get components of specific type, so I created that method:

template<typename comp>
comp * GetComponent()
{
    comp component;
    for (int i = 0; i < GetComponentsCount(); i++)
        if (typeid(*getComponents()[i]).name() == typeid(component).name())
            return (comp*)getComponents()[i];
    return NULL;
}

And when I later run it:

    if (obj.GetComponent<FirstClass>() != NULL)
        std::cout << "isn't null\n";
    else
        std::cout << "is null\n";

I know that vector saves pushed instances of IComponent inherited class, but it always prints me is null, because first typeid() shows me IComponent, and second shows me specific inherited component.

How to get a specific type from a vector? I tried polymorphism but that don't work, or I do something wrong:

template<typename comp>
void AddComponent() {
    comp c = comp();
    IComponent * i = &c;
    getComponents().push_back(i);
}

Upvotes: 2

Views: 834

Answers (3)

Potatoswatter
Potatoswatter

Reputation: 137770

typeid only works dynamically for polymorphic class types. For a class to be polymorphic, it needs a virtual function. Also, the destructor must be virtual if a class object may be owned by a base-class pointer, which may be the case here.

So, you should use,

class IComponent{
    virtual ~ IComponent() = default;
};

Edit: also, type_info::name() returns a C string pointer, which is not comparable using ==. A program using dynamic libraries may observe identical name strings at different addresses (though otherwise it's unusual). Compare the type_info objects instead. Also, typeid can take a type as an operand; you don't need to create an object to use it. So, you can do,

if (typeid(*getComponents()[i]) == typeid(comp))

Note that this checks for an exactly matching type. To find derived objects as well (permitting comp to be a base class), use dynamic_cast:

if (comp *p = dynamic_cast<comp *>(getComponents()[i])) {
    return p;
}

Upvotes: 6

Remy Lebeau
Remy Lebeau

Reputation: 595369

Make sure IComponent has at least one virtual method (the destructor will suffice, which should be virtual anyway when dealing with inheritance) and then use dynamic_cast instead of typeid.

class IComponent{
public:
    virtual ~IComponent() = default;
};

template<typename ComponentType>
ComponentType* GetComponent()
{
    auto &comps = getComponents();
    for (auto *item : comps) {
        ComponentType *comp = dynamic_cast<ComponentType*>(item);
        if (comp)
            return comp;
    }
    return nullptr;
}

Upvotes: 0

Edgar Rokjān
Edgar Rokjān

Reputation: 17483

I suggest you consider another possible workaround.

So you might add a function getType into the base class:

enum class Types {
    First,
    Second
};

class IComponent {
    public:
        virtual Types getType() const = 0;
        virtual ~IComponent() = default;
};

And then implement it in derived classes:

class First : public IComponent {
public:
    virtual Types getType() const override {
        return Types::First;
    }
};

class Second : public IComponent {
    public:
        virtual Types getType() const override {
            return Types::Second;
        }
};

After that searching and other tasks seems to be pretty easy:

const auto it = std::find_if(std::cbegin(components), std::cend(components),
    [](auto& ptr) {
        return ptr->getType() == Types::Second;
    }
);

wandbox example

Upvotes: 3

Related Questions