2
\$\begingroup\$

I'm currently making an Entity System, using C++, and I've questioned myself about how I should interface some methods. Specifically in my Entity and ComponentManager classes.

The ComponentManager, manages all components for all entities in a specific world. It can add/remove/determine if a Component exists, etc. Now to retrieve a component, I was thinking of using a template function, that way it looks neater and it doesn't have as many casts (outside the method). But then I want quick access to components through an Entity object. e.g.

entity.getComponent<VelocityComponent>().x = 30;

(and yes I know an Entity is just an ID, that's why I have my components in the ComponentManager class, and each Entity is represented by an ID).

i.e.

ComponentManager's get function: (Components are stored as a 2d array, with the first element being the ID of an entity and the 2nd element being the ID of a component's class type)

template <typename Comp> // Comp = Component
Comp* getComponent(Entity& e)
{
    return (Comp*)_components[e.getId()][Comp::GetClass().getId()];
}

And the Entity's get function:

template <typename Comp>
Comp* getComponent() 
{
      return getWorld().getComponentManager().getComponent<Comp>(*this); 
}

So if I was to use template functions I would just have 2 duplicates of template functions. Now of course, this goes the same for adding components to an Entity. So basically, is it a good idea to wrap a template function with another template function?

I can, alternatively make the the methods in the Entity system templates and make the methods in the component managers non-template functions, i.e.

Component* ComponentManager::getComponent(Entity& e, const Component::Class& componentClassType) const
{
     return _components[e.getId()][componentClassType.getId()];         
}

I'm confused on what I should do.:

\$\endgroup\$
0

2 Answers 2

1
\$\begingroup\$

There's nothing wrong with calling a templated routine within another templated routine, but you may want to just have one function that will give you a component instance from an entity ID so the interface is small and consistent (why have two routines that do the same thing?)

It's probably worth noting though that it's not safe to do things like this:

entity.getComponent<VelocityComponent>().x = 30;

unless all entities are guaranteed to have an instance of all components. You probably want to design your interface so that users do something like this.

VelocityComponent* velocity = entity.getComponent<VelocityComponent>();
if(velocity)
{
    // only do the lookup once and make sure this component exists.
    velocity->x = 30;
}

The component lookup that you're using now is pretty fast, but you may need to change it in the future to avoid the (numEntities * numComponentTypes) storage requirement.

\$\endgroup\$
-2
\$\begingroup\$

yes its fine to do that. You have just discovered compile-time polymorphism.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This doesn't appear to offer new information beyond the accepted answer from 6 years ago, "There's nothing wrong with calling a templated routine within another templated routine..." - do you have anything more you'd like to add to help users who have similar questions? \$\endgroup\$
    – DMGregory
    Commented Jan 13, 2019 at 3:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .