samedi 20 décembre 2014

for(auto e : elements) may cause one of elements to be wanted to be removed from vector

I have some classes:



struct Listenable{
virtual void removeListener(Listener * listener) = 0;
};

class Listener{
public: //that way example is simpler
unsigned myCode = 0;
Listenable * subject = 0;

Listener(unsigned myCode, Listenable * subject)
: myCode(myCode), subject(subject){}

void notify(unsigned value){
if(value == myCode){
a->removeListener(this);
}
}
};

class A : public Listenable{
public: //that way example is simpler
std::vector<Listener*> listeners;

void fun(unsigned value){
for(auto listener : listeners){
b->notify(value);
}
}

void removeListener(Listener * listener){
auto it = std::find(listeners.begin(), listeners.end(), listener);
if(it != listeners.end()){
listeners.erase(it);
}
}
};


and the code:



A a;

Listener * l1 = new Listener(5, a);
Listener * l2 = new Listener(7, a);

a.listeners.push_back(l1);
a.listeners.push_back(l2);
a.notify(3); //OK
a.notify(5); //error


I get the vector iterator not incrementable error in a.notify(5).


I know that it's because when I notify the l1 listener (inside of for loop of A::fun(5)), it decides to unsubscribe (call to A::removeListener).


But how to solve this? I want to iterate throw all the listeners and notify them about an event. I cannot assume if any of them (or how many of them) will want to remove itself from the list (it can happens as a reaction to event or somewhere else). I also cannot assume which circumstances will force specific Listener to call A::removeListener(this) and when.


I could change void notify(...) to bool notify(...) where return true would mean "please, remove me". But I cannot be sure that user won't call A::removeListener(this) from the class that inherit from Listener anyway.


Aucun commentaire:

Enregistrer un commentaire