Home > Software design >  C dynamic_cast dowcast fails
C dynamic_cast dowcast fails

Time:12-03


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. enter image description here

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 {...}
  • Related