Miaan
Miaan

Reputation: 1

Lambda and std::function causing dangling reference (segmentation fault)

I have a Entity system (I tried to recreated a simpler ECS without much worry on memory placement...)

EntityManager:

class EntityManager
{
    static std::unordered_map<std::type_index, std::list<Entity*>> entitiesMap;
public:
    static void RegisterEntity(std::type_index type, Entity* entity)
    {
        if (entitiesMap.find(type) == entitiesMap.end()) {
            entitiesMap.insert(std::pair<std::type_index, std::list<Entity*>>(
                type,
                std::list<Entity*>()
            ));
        }

        entitiesMap[type].push_back(entity);
    }

    template <class T>
    static void IterateEntities(std::function<void(T*)> method)
    {
        static_assert(std::is_base_of<Entity, T>::value, "T must be derived from Entity");
        
        std::type_index type = std::type_index(typeid(T));

        auto targetIterator = entitiesMap.find(type);
        if (targetIterator != entitiesMap.end()) {
            for (Entity* entity : targetIterator->second) {
                method(dynamic_cast<T*>(entity));
            }
        }
    }
};

std::unordered_map<std::type_index, std::list<Entity*>> EntityManager::entitiesMap;

Entity

class Entity
{
public:
    Entity(std::type_index type)
    {
        EntityManager::RegisterEntity(type, this);
    }

    virtual ~Entity() = default;
};

main.cpp

class DerivedEntity : Entity
{
public:
    DerivedEntity(int s) : Entity(typeid(DerivedEntity)) 
    {
        id = s;
    }

    int id;

    void Notify()
    {
        printf("Notitied: %d", id); // <--- Segmentation fault
    }
};

int main()
{
    DerivedEntity *test = new DerivedEntity(0);
    EntityManager::IterateEntities<DerivedEntity>([](DerivedEntity* t) { t->Notify(); });
}

I think the problem was dangling reference since I tried to log the address inside Notify() it returns null

void Notify()
{
    printf("Log: %p", this); // Log
    printf("Notitied: %d", id);
}

while I log the entity before the std::function call, it returns the right object I looking for

printf("Log: %p", entity); // Log
method(dynamic_cast<T*>(entity));

How to fix it?

Upvotes: 0

Views: 63

Answers (1)

Useless
Useless

Reputation: 67802

I can't immediately find a great duplicate for this, so I'll answer properly.

Entity is not polymorphic, because it doesn't have any virtual methods.

You can see from the documentation that dynamic_cast only works for down-casting (what you're trying to do) when the source is polymorphic.

You can and should confirm that this is your issue by writing a simple dynamic_cast right in main. Examining your segmentation fault in a debugger (with a debug build) would also have given you some idea where this nullptr was coming from.

You can make Entity polymorphic by simply adding a virtual destructor. You should usually have one of these anyway if you might want to manage ownership using base-class pointers.

Upvotes: 2

Related Questions