Reputation: 459
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
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
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
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;
}
);
Upvotes: 3