-1

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";
        }
    }
}
  • What is `d`? What does `d->getConsumers()` return? How do you add elements to whatever container you have? Could there be [object slicing](https://stackoverflow.com/questions/274626/what-is-object-slicing) along the way? Please try to create a [mre], and [edit] your question to show it. – Some programmer dude Dec 02 '21 at 14:31
  • @AndyG RenderEventConsumer doesn´t derive from any classes. – IchBinDerMika Dec 02 '21 at 14:33
  • Your title is misleading as well, as you don't do a "virtual_cast" but a `dynamic_cast`. – Some programmer dude Dec 02 '21 at 14:33
  • 3
    It's probably because `GameObject` is using `protected` inheretance from `RenderEventConsumer`, I don't remember the rules in such scenario exactly, but I think if you were to use a `dynamic_cast` outside of `GameObject` or it's children method, it will fail, change it to `public` and see if it works. – Kaldrr Dec 02 '21 at 14:34
  • I suspect you have a bug in the code you have not provided. A [mcve] would help a lot, and pay particular attention the *minimal* part. Help us help you. – Eljay Dec 02 '21 at 14:38

2 Answers2

0

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.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

Thanks to Kaldrr.
The solution was to derive publicly from RenderEventConsumer.

class GameObject : public TickEventConsumer, public RenderEventConsumer, public PhysicsTickEventConsumer {...}