Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Say in a game engine, you have Objects that composited of different components.

ID: 658626 • Letter: S

Question

Say in a game engine, you have Objects that composited of different components. One of these is a RenderComponent which can be either a Mesh, a Sprite or a Light.

Now, all these are vastly different classes with non-complatible interfaces, so deriving them all from RenderComponent makes little sense. Also, an Object can only have one RenderComponent at a time, so you saving three pointers in Object, two of which must always be nullptr, seems wasteful. So what I came up with is using RenderComponent to store a void* pointer and the type.

class RenderComponent{
public:
    enum class RenderType{
        Mesh, Sprite, Light
    };

    RenderType getType(){ return type_; }

    Mesh* asMesh(){
        if(type_ == RenderType::Mesh)
            return static_cast<Mesh*>(pointer_.get())
        else
            return nullptr;
    } /* the same stuff for Sprite and Light */

    RenderComponent(unique_ptr<Mesh>&& pointer) :
        pointer_(std::move(pointer)),
        type_(RenderType::Mesh)
    {} /* the same stuff for Sprite and Light */

private:
    unique_ptr<void> pointer_;
    RenderType type_;
}
It should work as intended, but I feel like this is an anti-pattern. I'm basically discarding all typesafe aspects of C++ and then reimplement them myself. This can't be right.

So, is this an anti-pattern? What are common workarounds for this problem? Am I missing something obvious? Or is this a known, but (for good reason) only rarely used pattern?

Explanation / Answer

The correct way to approach this is to use RTTI and dynamic_cast. This does nearly the same thing as what you are doing, but in a type-safe, language approved manner. (dynamic_cast throws if you try to case objects into things they are not related to.)

One reason people write code like this is that dynamic_cast came into the language after people had been using it a while. Too often, developers don't keep up with language improvements and continue doing things they way they are used to. So in 1994, this might have been perfectly good code.

The other reason people write code like this is because of the widespread belief that RTTI hurts performance. The thought is that because RTTI has to store type information for every single class, it "wastes memory". You often see this attitude with people who do embedded development, or in game development. Also, dynamic_cast requires exception handling, and the same groups often turn this off exception handling.

There is something behind this in that RTTI can increase your memory footprint. This particularly happens when you make heavy use of templated classes like the STL as these can cause the compiler to create large numbers of classes under the covers, all of which get type information. On one project I worked on like this, we saw nearly half a megabyte lost to type information. My feeling, though, is that if you have a tight memory budget, you might be better off refactoring the templated classes into something that don't use templates.

Because of the general dislike of RTTI in the game industry, third party engines will almost never use it. This is more about supplying what the customer is asking for than good engineering.

This all requires an inheritance tree, of course. You say:

Now, all these are vastly different classes with non-complatible interfaces, so deriving them all from RenderComponent makes little sense.

But I find this questionable, because your first line is:

One of these is a RenderComponent which can be either a Mesh, a Sprite or a Light.

Emphasis mine. "be" seems to denote an "is-a" relationship, which is a classic sign of a class hierarchy.

I don't think this is necessarily an "anti-pattern", though, as this is a way that many successful projects have approached the problem, and it's something that ended up being codified into the language.

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote