3 hours ago, Tim Leijten said:
5 hours ago, SyncViews said:
Sounds more like a `create`. I think the problem here is the "deleted at any point". What is able to delete it, why is it?
E.g. something a player does causes the object to delete itself. Why does everyone think it has something to do with threading?
Because of how you wrote "no way to check if the returned pointer is valid" and "could be deleted at any point".
In a single threaded environment, "deleted at any point", should not be true, and you should be able to guarantee a returned pointer is safe.
Entity *target = map->get_nearest_enemy(this->faction(), this->position());
// target should be valid, and there should be no way for it to become unexpected invalid, within this code block
if (target->type()->valid_assassination_target())
{
target->kill(); // Now, maybe the pointer is invalid
}
The "something a player does causes the object to delete itself" can't happen in the middle of that block if it is only single threaded, and so the raw pointer can be used safely, keeping it over a longer duration of course has other considerations.
The advantage of using a unique ID / "handle" there, is because of memory reuse you can't safely validator a pointer at a later time. But being able to store an ID between frames you can, and then you can retrieve it again later safely.
Entity *target = map->get_entity_by_id(target_id)
if (target != nullptr)
{
// target still valid, raw pointer is safe as before
}
else
{
// target is gone
target_id = INVALID_ENTITY_ID; // fairly easy to have some sentinel value that acts like a null pointer
}
In a more complex environment, you could turn that into your own custom smart pointer, I did that previous as an optimisation on "get_entity_by_id" in a design where I stored things in std::vector like arrays and compacted them (meaning objects could move memory address to keep an efficient memory layout, although that was part of an ECS design, so "Entity*" like OOP didn't exist at all)