Tommy-Xavier Robillard
Tommy-Xavier Robillard

Reputation: 339

Template classes and calling different constructors depending on iteration

I am learning ECS and I am now trying to implement components for my project.

So to put you in context, I have an aquarium, and many components in it (let's say seaweeds and fish). Both have an age, but only fishes have a race.

I have a class for general_components (age and other stuff) and a class for fish-specific components (race, sex, etc).

I made a template components class with a create method that looks like that:

template<typename ConcreteComponent> // ConcreteComponent is attached to the class
ConcreteComponent& Components<ConcreteComponent>::create( entity e ) {
    m_components.push_back( ConcreteComponent(e) );
    return m_components.back();
}

The problem I have is I want to be able to call different constructors depending on what class I have (instead of entity e right here, which is generic to every class in my setup), but while staying the most efficient way (so with templates and no copy paste for every class). For my problem it wouldn't be the end of the world but in general if I faced this again I want to be prepared.

Is there a way to call a create function with different parameters?

For example:

A<fishComponents> myClassIter;
myClassIter.create("name", age, race, sex)

For fishes would pass "name",age,race,sex to ConcreteComponent() constructor instead of only e (And I have a "name",age,race,sex constructor for fishComponents).

TL;DR: in a template class method, is it possible to pass a different number and nature of values to the constructor of the class used depending on the parameters

template<typename A>
void myClass<A>::create( list_of_parameters) {
A(list_of_parameters) /*calls the constructor of the template class A */
}

I saw something like that in C but it was recommended not to touch it as it was dated and not used any more.

Upvotes: 1

Views: 2032

Answers (2)

asdf
asdf

Reputation: 221

You had better use the inheritance of class which is used in Oriented-Object programming.

Build a base class and its inherited classes, so the generated inherited classes' objects can produced by their different properties dynamically.

Upvotes: 1

Stephen Newell
Stephen Newell

Reputation: 7863

The solution here is to use parameter packs and std::forward to pass along whatever arguments come in to the real constructor. A complete but simplified example (no data structures and one argument per type) is below:

#include <iostream>

class Fish {
public:
    Fish(std::string const &name) {
        std::cout << "Making a fish named " << name << '\n';
    }
};

class Seaweed {
public:
    Seaweed(int length) {
        std::cout << "Making a seaweed that's " << length << " feet long\n";
    }
};

template <typename ConcreteComponent, typename ...ARGS>
ConcreteComponent create(ARGS && ...args) {
    return ConcreteComponent(std::forward<ARGS>(args)...);
}

int main() {
    create<Fish>("Bob");
    create<Seaweed>(42);
    return 0;
}

Output:

$ ./forwarding
Making a fish named Bob
Making a seaweed that's 42 feet long

It's worth reading about std::forward, but what we're basically doing here is taking everything that comes in to create and passing it along to the type's constructor, while also preserving properties about the type (e.g., whether it's a temporary or not). Thus, you can pass anything, so long as there's a valid constructor to pass things on to.

My code was tested with g++-7.3.0 using C++11, 14, and 17.

Upvotes: 2

Related Questions