Usiten Usiten - 2 months ago 15
C++ Question

C++ Component System Issue

I'm trying to get a small Entity/Component System to work in C++. So far it's ok, it doesn't have much to it, but I'm facing an issue.

Here is my current code:

class Entity {
public:
// [...]
template<typename T, typename ...Args>
bool AddComponent(Args... args) {
std::type_index tid = std::type_index(typeid(T));
if (_Components[tid] == nullptr) {
_Components[tid] = std::make_shared<T>(args...);
return true;
}
return false;
}

template<typename T>
std::shared_ptr<T> GetComponent() {
// Issue here
std::type_index tid = std::type_index(typeid(T));
return std::dynamic_pointer_cast<T>(_Components[tid]); // return nullptr if no component
}

template<typename T>
bool RemoveComponent() {
std::type_index tid = std::type_index(typeid(T));
if (_Components.erase(tid) == 1) return true;
return false;
}

private:
std::map<std::type_index, std::shared_ptr<Component>> _Components;
};


As seen above, it's pretty straightforward, each component is derived from a base class
Component
and is used as follow:

class Component;
class MyComponent: public Component {
// [...]
MyComponent(int foo_): foo(foo_) {}
int foo;
}

Entity e;
e.AddComponent<MyComponent>(42);
std::cout << e.GetComponent<MyComponent>()->foo; // 42
e.RemoveComponent<MyComponent>();
std::cout << e.GetComponent<MyComponent>()->foo; // Error


The error is logic and I understand why, but my question would be how to prevent a error when calling a
Component
that has been removed, and thus is
nullptr
without crashing the program ?

Is a simple try/catch enough ? How should it be implemented here ?

Answer

You can either check the pointer:

auto ptr = e.GetComponent<MyComponent>();
if(ptr) ptr->foo();

Or add a HasComponent method:

template<typename T>
bool HasComponent() {
    std::type_index tid = std::type_index(typeid(T));
    return _Components.find(tid) != _Components.cend();
}

You can also modify GetComponent so as it doesn't insert a nullptr in the map:

template<typename T>
std::shared_ptr<T> GetComponent() {
    std::type_index tid = std::type_index(typeid(T));
    return (_Components.find(tid) == _Components.cend()) ? nullptr : std::dynamic_pointer_cast<T>(_Components[tid]);
}