Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ map iteration with deletion

I couldn't find an instance of how to do this, so I was hoping someone could help me out. I have a map defined in a class as follows:

std::map<std::string, TranslationFinished> translationEvents;

TranslationFinished is a boost::function. I have a method as part of my class that iterates through this map, calling each of the functions like so:

void BaseSprite::DispatchTranslationEvents()
{
    for(auto it = translationEvents.begin(); it != translationEvents.end(); ++it)
    {
        it->second(this);
    }
}

However it's possible for a function called by it->second(this); to remove an element from the translationEvents map (usually itself) using the following function:

bool BaseSprite::RemoveTranslationEvent(const std::string &index)
{
    bool removed = false;
    auto it = translationEvents.find(index);
    if (it != translationEvents.end())
    {
        translationEvents.erase(it);
        removed = true;
    }
    return removed;
}

doing this causes a debug assertion fail when the DispatchTranslationEvents() tries to increment the iterator. Is there a way to iterate through a map safely with the possibility that a function call during the iteration may remove an element from the map?

Thanks in advance

EDIT: Accidently C/Pd the wrong Remove Event code. Fixed now.

like image 427
Megatron Avatar asked Apr 15 '11 02:04

Megatron


1 Answers

map::erase invalidates the iterator being deleted (obviously), but not the rest of the map. This means that:

  • if you delete any element other than the current one, you're safe, and
  • if you delete the current element, you must first get the next iterator, so you can continue iterating from that (that's why the erase function for most containers return the next iterator). std::map's doesn't, so you have to do this manually)

Assuming you only ever delete the current element, then you could simply rewrite the loop like this:

for(auto it = translationEvents.begin(); it != translationEvents.end();)
{
    auto next = it;
    ++next; // get the next element
    it->second(this); // process (and maybe delete) the current element
    it = next; // skip to the next element
}

Otherwise (if the function may delete any element) it may get a bit more complicated.

like image 55
jalf Avatar answered Oct 14 '22 10:10

jalf