val
val

Reputation: 749

Keep track of instantiated templated types C++

I am reworking an Entity Component System's Entity Manager. Since components don't have overlapping functionalities, i dont want them to have a shared base I could store.

So i came up with something like this:

#include <vector>
#include <memory>
#include <iostream>

class Component1{};
class Component2{};
class Component3{};

class Manager{
public:
  template<typename T> static std::vector<std::shared_ptr<T>> component;

  template<typename T> static std::shared_ptr<T> getComponent(int nth){
    return component<T>[nth];
  }

  template<typename T> static std::shared_ptr<T> addComponent(int nth){
    return component<T>[nth] = shared_ptr<T>(new T());
  }

  static void addEntity(){
    // push back a nullptr for every instantiated component<>
  }

  static void removeEntity(int nth){
    // set the nth index of every component<> to nullptr
  }
};

template<typename T>
std::vector<std::shared_ptr<T>> Manager::component = std::vector<std::shared_ptr<T>>();

int main(){
  Manager::component<Component1>;
  Manager::component<Component2>;
  Manager::component<Component3>;

  Manager::addEntity();
  auto cmp2 = Manager::getComponent<Component2>(0);

  Manager::removeEntity(0);

  std::cin.get();
  return 0;
}

How can I iterate over the instantiated components for the two functions? Tried using a vector of type_info's to store the Component types, but I could never the get the proper type out of them to use as a template argument.

Upvotes: 2

Views: 435

Answers (1)

Pedro Boechat
Pedro Boechat

Reputation: 2576

You could start by using a template meta-programming trick to get a unique ID for your types. Then you could use a map with unique type ids instead of your variable template vectors. By introducing polymorphism with a base Component class, coupled with a static_cast (to reduce run-time costs), you could easily re-implement previous addComponent and getComponent methods. Because of the map access, they're going to be slightly more costly, but in the end you can implement addEntity and removeEntity by iterating through the map, doing exactly what you wanted.

Here's my implementation of the aforementioned ideas:

#include <vector>
#include <map>
#include <memory>
#include <iostream>

typedef void(*unique_id_type)();

template <typename... Arguments>
struct IdGen {
    static constexpr inline unique_id_type get_unique_id()
    {
        return &IdGen::dummy;
    }

private:
    static void dummy() {};

};

class Component {};
class Component1 : public Component {};
class Component2 : public Component {};
class Component3 : public Component {};

class Manager {
public:
    static std::map<unique_id_type, std::vector<std::shared_ptr<Component>>> components;

    template<typename T> static std::shared_ptr<T> getComponent(int nth) {
        return std::static_pointer_cast<T>(components[IdGen<T>::get_unique_id()][nth]);
    }

    template<typename T> static std::shared_ptr<T> addComponent(int nth) {
        return std::static_pointer_cast<T>(components[IdGen<T>::get_unique_id()][nth] = std::shared_ptr<T>(new T()));
    }

    static void addEntity() {
        for (auto& component : components)
            component.second.push_back(nullptr);
    }

    static void removeEntity(int nth) {
        for (auto& component : components)
            component.second[nth] = nullptr;
    }
};

std::map<unique_id_type, std::vector<std::shared_ptr<Component>>> Manager::components = {
    { IdGen<Component1>::get_unique_id(), {} },
    { IdGen<Component2>::get_unique_id(), {} },
    { IdGen<Component3>::get_unique_id(), {} },
};

int main() {
    Manager::addEntity();
  auto cmp2 = Manager::getComponent<Component2>(0);

  Manager::removeEntity(0);

  std::cin.get();
  return 0;
}

PS = This code uses C++11 features such as constexpr and list initialization, but since you were already using C++14 (even though you didn't tag your question as C++14), I think that's not a problem.

PS 2 = Since I'm downcasting with a static_cast, you shouldn't use virtual inheritance with your components (read why).

Upvotes: 3

Related Questions