While writing my first big project in C , I encountered a problem which I wasn´t able to solve using google and documentation alone.
I cannot figure out, why this dynamic_cast fails, even though r is pointing to a MeshRenderer Object.
for (RenderEventConsumer* r : d->getConsumers())
{
glUseProgram(mPickingShader->apiID);
MeshRenderer* m = dynamic_cast<MeshRenderer*>(r); //returns nullptr
if (m)
{
glUniform1ui(uPickingID, m->getOwner()->getID());
m->getMesh()->getUtillityBuffer().draw();
}
}
The class RenderEventConsumer has a virtual method and is a base of MeshRenderer.
class MeshRenderer : public Component {...}
class Component : public GameObject {...}
class GameObject : protected TickEventConsumer, protected RenderEventConsumer, protected PhysicsTickEventConsumer {...}
According to Visual Studio the vftable of r is correct.
PS: This is my first question on stackoverflow, please let me know if I violated any guideline or am missing relevant information.
EDIT: Although I know the answer now, I reproduced the error with a standalone example for clarity:
#include <vector>
#include <iostream>
class RenderEventConsumer
{
virtual void onRender() {};
};
class RenderEventDispatcher
{
std::vector<RenderEventConsumer*> mConsumers;
public:
const std::vector<RenderEventConsumer*>& getConsumers()
{
return mConsumers;
}
void registerRenderEventConsumer(RenderEventConsumer* consumer)
{
mConsumers.push_back(consumer);
}
};
class GameObject : protected RenderEventConsumer {}; //changing this to public fixes dynamic_cast
class Component : public GameObject {};
class MeshRenderer : public Component
{
public:
void setup(RenderEventDispatcher& d)
{
d.registerRenderEventConsumer(this);
}
void onRender() override { }
};
int main()
{
RenderEventDispatcher d;
MeshRenderer* pt = new MeshRenderer();
pt->setup(d);
for (RenderEventConsumer* r : d.getConsumers())
{
MeshRenderer* m = dynamic_cast<MeshRenderer*>(r);
if (m)
{
std::cout << "not nullptr\n";
}
else
{
std::cout << "nullptr\n";
}
}
}
CodePudding user response:
Just because RenderEventConsumer
is a base MeshRenderer
, that doesn't mean that all classes derived from RenderEventConsumer
are also (derived from) MeshRenderer
.
If MeshRenderer
isn't (a base of) the dynamic type of the pointed object, then dynamic_cast
will return null.
Here is an example of how that may happen:
struct another_derived : RenderEventConsumer {} instance;
RenderEventConsumer* r = &instance;
// null, even through r is RenderEventConsumer*
dynamic_cast<MeshRenderer*>(r);
If you find that you need to use dynamic_cast
, that implies that the design is broken. Consider using for example the visitor pattern instead.
CodePudding user response:
Thanks to Kaldrr.
The solution was to derive publicly from RenderEventConsumer.
class GameObject : public TickEventConsumer, public RenderEventConsumer, public PhysicsTickEventConsumer {...}