0

I have this code where I am trying to return a component which is a base class that is inherited by many different "components". I want to then use this 'getComponent' method to call a function specific to that inherited component. Is there any way to do this?

Component& GameObject::getComponent(const std::string &name)
{
    for(Component* c : components){
        std::cout << c->name << std::endl;
        if(c->name == name){
            return *c;
        }
    }
    throw std::invalid_argument( "GameObject does not have Component: " + name);

}
guiege
  • 63
  • 3
  • 7
    "call a function specific to that inherited component" This is the same mistake novice OO programmers make again and again and again and again. You just don't do that. If you are working with components, you only call functions that are common to **all** components. You **intentionally forget** that there are specific components with their specific methods. This is the gist, the essence, the soul, the body, the everything of the OO programming. – n. m. could be an AI Dec 21 '22 at 18:17
  • Usually you specify a `virtual` function in the base class that's overridden by the derived classes and no longer care exactly what derived class you're dealing with. If you can't do this you're probably running up against the [Liskov Substitution Principle](https://stackoverflow.com/q/56860/4581301) and making your life harder than it needs to be. There are, of course, exceptions where you really do want to do this, but you should have a really good justification that weighs the pain and anguish caused by both options. – user4581301 Dec 21 '22 at 18:18
  • 1
    the usual way would be `T& getComponent(...)`. whether it's a good approach depend on the real case. – apple apple Dec 21 '22 at 18:19
  • 1
    `dynamic_cast`, but as others have said, if you think you need a cast then something went wrong already before. You should step back and reconsider your design – 463035818_is_not_an_ai Dec 21 '22 at 18:20

1 Answers1

1

Assuming you can't change the "function specific to that inherited component" into a virtual method of the base class, then you basically have no choice but to cast the returned Component& into the correct type. But you can do that cast in 1 of 2 different places:

  • you can make the caller cast the result of getComponent(), eg:

    Derived *comp = dynamic_cast<Derived*>(&go.getComponent("name"));
    // will return nullptr if the cast fails...
    if (comp) comp->doSomething();
    

    Alternatively:

    Derived &comp = dynamic_cast<Derived&>(go.getComponent("name"));
    // will throw std::bad_cast if the cast fails...
    comp.doSomething();
    

    Or, if you know the exact type of the component ahead of time:

    Derived &comp = static_cast<Derived&>(go.getComponent("name"));
    comp.doSomething();
    
  • you can make getComponent() itself do the cast before returning, eg:

    template<typename T>
    T& GameObject::getComponent(const std::string &name)
    {
        for(Component* c : components){
            std::cout << c->name << std::endl;
            if(c->name == name){
                return dynamic_cast<T&>(*c);
                // or:
                return static_cast<T&>(*c);
            }
        }
        throw std::invalid_argument("GameObject does not have Component: " + name);
    }
    
    Derived &comp = go.getComponent<Derived>("name");
    comp.doSomething();
    
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770