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.
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.
Generally speaking it is frowned upon to modify the collection during iteration. Many collections invalidate the iterator when the collection is modified, including many of the containers in C# (I know you're in C++). You can create a vector of events you want removed during the iteration and then remove them afterwards.
After reading all other answers, I am at an advantage here... But here it goes.
However it's possible for a function called by it->second(this); to remove an element from the translationEvents map (usually itself)
If this is true, that is, a callback can remove any element from the container, you cannot possibly resolve this issue from the loop itself.
Deleting the current callback
In the simpler case where the callback can only remove itself, you can use different approaches:
// [1] Let the callback actually remove itself
for ( iterator it = next = m.begin(); it != m.end(); it = next ) {
++next;
it->second(this);
}
// [2] Have the callback tell us whether we should remove it
for ( iterator it = m.begin(); it != m.end(); ) {
if ( !it->second(this) ) { // false means "remove me"
m.erase( it++ );
} else {
++it;
}
}
Among these two options, I would clearly prefer [2], as you are decoupling the callback from the implementation of the handler. That is, the callback in [2] knows nothing at all about the container in which it is held. [1] has a higher coupling (the callback knows about the container) and is harder to reason about as the container is changed from multiple places in code. Some time later you might even look back at the code, think that it is a weird loop (not remembering that the callback removes itself) and refactor it into something more sensible as for ( auto it = m.begin(), end = m.end(); it != end; ++it ) it->second(this);
Deleting other callbacks
For the more complex problem of can remove any other callback, it all depends on the compromises that you can make. In the simple case, where it only removes other callbacks after the complete iteration, you can provide a separate member function that will keep the elements to remove, and then remove them all at once after the loop completes:
void removeElement( std::string const & name ) {
to_remove.push_back(name);
}
...
for ( iterator it = m.begin(); it != m.end(); ++it ) {
it->second( this ); // callback will possibly add the element to remove
}
// actually remove
for ( auto it = to_remove.begin(); it != to_begin.end(); ++it ) {
m.erase( *it );
}
If removal of the elements need to be immediate (i.e. they should not be called even in this iteration if they have not yet been called), then you can modify that approach by checking whether it was marked for deletion before executing the call. The mark can be done in two ways, the generic of which would be changing the value type in the container to be a pair<bool,T>, where the bool indicates whether it is alive or not. If, as in this case, the contained object can be changed you could just do that:
void removeElement( std::string const & name ) {
auto it = m.find( name ); // add error checking...
it->second = TranslationFinished(); // empty functor
}
...
for ( auto it = m.begin(); it != m.end(); ++it ) {
if ( !it->second.empty() )
it->second(this);
}
for ( auto it = m.begin(); it != m.end(); ) { // [3]
if ( it->second.empty() )
m.erase( it++ );
else
++it;
}
Note that since a callback can remove any element in the container, you cannot erase as you go, as the current callback could remove an already visited iterator. Then again, you might not care about leaving the empty functors for a while, so it might be ok just to ignore it and perform the erase as you go. Elements already visited that are marked for removal will be cleared in the next pass.
My solution is to first create a temporary container, and swap it with the original one. Then you can iterator through the temporary container and insert the ones you want to keep to the original container.
void BaseSprite::DispatchTranslationEvents()
{
typedef std::map<std::string, TranslationFinished> container_t;
container_t tempEvents;
tempEvents.swap(translationEvents);
for(auto it = tempEvents.begin(); it != tempEvents.end(); ++it)
{
if (true == it->second(this))
translationEvents.insert(it);
}
}
And the TranslationFinished functions should return true if it want to be keeped and return false to get removed.
bool BaseSprite::RemoveTranslationEvent(const std::string &index)
{
bool keep = false;
return keep;
}
There should be a way for you to erase a element during your iteration, maybe a little tricky.
for(auto it = translationEvents.begin(); it != translationEvents.end();)
{
//remove the "erase" logic from second call
it->second(this);
//do erase and increase the iterator here, NOTE: ++ action is very important
translationEvents.erase(it++);
}
The iterator will be invalid once the element is removed, so you can not use that iterator to do increase action anymore after you remove it. However, remove an element will not affect other element in map implementation, IIRC. So suffix ++ will copy the iter first and increase the iterator right after that, then return the copy value, which means iterator is increased before erase action, this should be safe for you requirement.
You could defer the removal until the dispatch loop:
typedef boost::function< some stuff > TranslationFunc;
bool BaseSprite::RemoveTranslationEvent(const std::string &index)
{
bool removed = false;
auto it = translationEvents.find(index);
if (it != translationEvents.end())
{
it->second = TranslationFunc(); // a null function indicates invalid event for later
removed = true;
}
return removed;
}
protect against invoking an invalid event in the loop itself, and cleanup any "removed" events:
void BaseSprite::DispatchTranslationEvents()
{
for(auto it = translationEvents.begin(); it != translationEvents.end();)
{
// here we invoke the event if it exists
if(!it->second.empty())
{
it->second(this);
}
// if the event reset itself in the map, then we can cleanup
if(it->second.empty())
{
translationEvents.erase(it++); // post increment saves hassles
}
else
{
++it;
}
}
}
one obvious caveat is if an event is iterated over, and then later on deleted, it will not get a chance to be iterated over again to be deleted during the current dispatch loop.
this means the actual deletion of that event will be deferred until the next time the dispatch loop is run.
The problem is ++it follows the possible erasure. Would this work for you?
void BaseSprite::DispatchTranslationEvents()
{
for(auto it = translationEvents.begin(), next = it;
it != translationEvents.end(); it = next)
{
next=it;
++next;
it->second(this);
}
}
Related
I have a bunch of elements stored in some container. Their order does not matter for me.
I iterate over my container and check some predicate - P for each of the elements. If P is true - remove the element from the container. If P is false - just go to the next one.
If at least one element was deleted during the iteration I repeat this process. There is a chance that on a new iteration the P will be true for the elements for which it was false during previous iterations.
I've written a code for this
std::unordered_map<T, T> container;
auto it = container.begin();
while (it != container.end()) {
if (predicate(*it)) {
it = container.erase(it);
} else {
it++;
}
}
I have a question:
Is there a better way to do this (both in terms of clean code and it's time efficiency) considering I have about 500 elements in my container.
Use std::erase_if() in a loop:
while (std::erase_if(your_set, your_predcate))
/**/;
If you don't have C++20, don't despair. Cppreference.com gives an example implementation too.
If it proves to be a bottleneck, hand-rolling your own all_erase_if() with a specialization for node-based containers might be useful:
template <class T>
constexpr bool has_node_type = requires { typename T::node_type; };
template <class T>
constexpr bool is_node_based = has_node_type<T>;
template <class C, class P>
auto all_erase_if(C& c, F f) requires is_node_based<C> {
const auto old_size = std::size(c);
if (!old_size)
return old_size;
auto it = std::begin(c), stop = std::begin(c);
do {
while (f(*it)) {
it = stop = c.erase(it);
if (it != std::end(c))
/**/;
else if (std::empty(c))
return old_size;
else
it = stop = std::begin(c);
}
if (++it == std::end(c))
it = std::begin(c);
} while (it != stop);
return old_size - std::size(c);
}
template <class C, class P>
auto all_erase_if(C& c, F f) requires !is_node_based<C> {
const auto old_size = std::size(c);
while (std::erase_if(c, std::ref(f)))
/**/;
return old_size - std::size(c);
}
You want to circle-iterate over the container until you have done a complete pass where you don't remove anything.
template<class C, class F>
void multi_pass_erase( C& c, F&& f )
{
auto stop_at = c.end();
auto it = current;
while (true)
{
if (c.empty())
return;
if (f(*it))
{
it = c.erase(it);
if (it == c.begin())
stop_at = c.end();
else
stop_at = it;
}
else
{
++it;
if (it == stop_at)
return;
}
if (it == c.end())
it = c.begin();
}
}
At the start of the loop, it refers to the next element to test, and only refers to end if the container is empty.
So if the container is empty, return.
stop_at tracks the element that, if we reach it, we have gone over the entire container and not found something to filter.
If we remove something, we note that the proper place to stop is after the element we erase.
If we don't remove something, we advance the iterator, and check if we should stop.
Then, if we have reached the end of the container, we go back to the start.
There is a bit of careful work where we test for "stop" before we move it from end back to begin, so we should never store stop_at as referring to begin().
Now lets compare it to
while (true) {
if (!std::erase_if( set, test ))
break;
}
Imagine if each cycle removes one element. This could require O(n^2) time.
multi_pass_erase doesn't do amazingly better in this case. If each element causes the previous one to be erased, multi_pass_erase doesn't shave off any visits whatsoever; in both cases, you have to visit every non-removed node before you find the next node to remove.
Basically, all of the fancyness to multi_pass_erase shaves off an average of half an iteration of the set each call whenever there is at least 1 erase, as if we assume the last erase is randomly positioned, we skip doing an average of half of the container.
The added complexity is probably not worth it.
But can we write something more complex and more efficient?
Usually when you delete something that could cause other things to need deletion, you can get information about those other things.
Consider tracking that information, and only looking at those elements, instead of the entire list again.
template<class C, class Test, class Dependencies>
void dependent_erase( C& c, Test&& t, Dependencies&& d ) {
auto it = c.begin();
using key_type = typename C::key_type;
std::vector<key_type> todo_list;
while (it != c.end())
{
if (t(*it)) {
d( *it, &todo_list );
it = c.erase(it);
} else {
++it;
}
}
// remove duplicates:
std::vector<key_type> next_todo_list;
while (!todo_list.empty()) {
// better to shrink the list and ask f(x) less often
std::sort(todo_list.begin(), todo_list.end());
todo_list.erase( std::unique(todo_list.begin(), todo_list.end()), todo_list.end() );
for (auto&& todo : todo_list) {
auto it = c.find( todo );
if (f(*it))
{
d( *it, &next_todo_list );
c.erase(it);
}
}
todo_list = std::move( next_todo_list );
next_todo_list.clear();
}
}
here we have our Test t (do we want to delete this item?) If we do, we call d( item, vector* ) and store any direct dependencies we'd want to retest there.
We then go over the container, removing things as needed. Then we go over the dependencies and remove anything mentioned that should go away, repeatedly until we no longer find new items to delete.
If we assume your code is a bunch of nodes with references to other nodes, and you are doing garbage collection, this should be far better in many cases.
...
Code not tested or even compiled. But I've done this before, so it might work. It should at least work as pseudo-code.
If you expect a LOT of dependencies per deleted node, and lots of overlap, a set-based todo list might be better than a vector one. Ie, if you have N elements removed each with M dependencies, that vector grows to NM size. But if M tends to be small and not have much overlap with other elements, then the vector will be much faster than a node-based set.
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) inside of his custom notify(...){...} (from the class that inherit from Listener) anyway.
There's only one way to go if the vector might be changed while you iterate over it:
Iterate over a copy!
Unless, of course, you can just change the data-structure.
This seems like a probable use case for std::list, where iterators don't become invalidated when the list is changed (unless the thing they refer to is removed).
If you had a std::list<Listener*>, you could step through the list with two iterators current and next (for example), keep next one ahead of current, notify *current in every iteration and be sure that next will still be a valid iterator afterwards. Then set current = next; ++next;, and the issue is neatly sidestepped.
An alternative is to mark the element to suppress, and remove it after the loop, something like:
class A : public Listenable{
public: //that way example is simpler
std::vector<Listener*> listeners;
void fun(unsigned value){
for (auto listener : listeners){
if (listener) { // not marked as deleted
listener->notify(value);
}
}
// remove 'mark_as_deleted' listeners
listeners.erase(std::remove(listeners.begin(), listeners.end(), nullptr),
listeners.end();
}
void removeListener(Listener* listener){
auto it = std::find(listeners.begin(), listeners.end(), listener);
if (it != listeners.end()){
*it = nullptr; // mark as deleted.
}
}
};
If you can't control whether your listeners may try to remove themselves from thew collection, you should consider using a container with stable iterators. One good example would be stable_vector class from Boost (see the documentation here: http://www.boost.org/doc/libs/release/doc/html/container/non_standard_containers.html#container.non_standard_containers.stable_vector).
Its memory footprint is of course bigger than one of std::vector, but overall algorithmic complexity is same. A good feature you get is particular element iterator stays valid as long as element is present in the container. You can rewrite your iteration like this to make it tolerate removals:
for (auto iter = listeners.begin(); iter != listeners.end(); ) // note we don't auto increment here
auto next_iter = iter + 1; // remember the next element
iter->do_something(); // may remove itself from the container
iter = next_iter;
}
EDIT: Or, as wintermute suggested, you may use std::list as the stable container, although it has pretty worse performance when it comes to iteration, and its memory usage is way less efficient than one of any vector.
Add a boolean flag isNotifying to the class A.
Set this flag to true at the beginning of fun and to false at the end of fun.
Inside removeListener, check the flag. If it is false, simply remove the listener as you are doing now. Otherwise, add the listener to a second vector of listeners to be removed in the future.
At the very end of fun, remove all listeners from said vector of listeners. Then clear said vector.
If fun needs to be re-entrant, use an int instead of a boolean flag and count up/down.
Here's my code for updating a list of items in a vector and removing some of them:
std::vector<Particle*> particles;
...
int i = 0;
while ( i < particles.size() ) {
bool shouldRemove = particles[ i ]->update();
if ( shouldRemove ) {
delete particles[ i ];
particles[ i ] = particles.back();
particles.pop_back();
} else {
i++;
}
}
When I find an item that should be removed, I replace it with the last item from the vector to avoid potentially copying the rest of the backing array multiple times. Yes, I know it is premature optimization...
Is this a valid way of removing items from the vector? I get some occasional (!) crashes somewhere around this area but can't track them down precisely (LLDB fails to show me the line), so I would like to make sure this part is OK. Or is it... ?
UPDATE: I found the bug and indeed it was in another part of my code.
Yes, this is a valid way. But if it is not a performance bottleneck in your program then it's better to use smart pointers to manage the lifetime of Particle objects.
Take a look at std::remove_if.
Also, might be good to use a shared pointer as it may make life easier :-)
typedef std::shared_ptr< Particle > ParticlePtr;
auto newend = std::remove_if( particles.begin(), particles.end(), [](ParticlePtr p) {return p->update();} );
particles.erase( newend, particles.end() );
You are iterating over an STL vector, so use iterators, it's what they're for.
std::vector<Particle*>::iterator particle = particles.begin();
while ( particle != particles.end() ) {
bool shouldRemove = particle->update();
if ( shouldRemove ) {
particle = particles.remove(particle); //remove returns the new next particle
} else {
++particle;
}
}
Or, even better, use smart pointers and the erase/remove idiom. Remove_if itself does as you describe, moving old members to the back of the vector and returning an iterator pointing to the first non-valid member. Passing this and the vector's end() to erase allows erase to erase all the old members as they are in a contiguous block. In your scenario, you would have to delete each before calling erase:
auto deleteBegin = std::remove_if(
particles.begin(), particles.end(),
[](Particle* part){ return part->update();}));
for(auto deleteIt = deleteBegin; deleteIt != particles.end(); ++deleteIt)
delete *deleteIt;
std::erase(deleteBegin, particles.end());
Or pre C++11:
bool ShouldDelete(Particle* part) {
return part->update();
}
typedef vector<Particle*> ParticlesPtrVec;
ParticlesPtrVec::iterator deleteBegin = std::remove_if(
particles.begin(), particles.end(), ShouldDelete);
for(ParticlesPtrVec::iterator deleteIt = deleteBegin;
deleteIt != particles.end(); ++deleteIt)
delete *deleteIt;
std::erase(deleteBegin, particles.end());
Then test the whole code for performance and optimise wherever the actual bottlenecks are.
I don't see any direct issue in the code. You are probably having some issues with the actual pointers inside the vector.
Try running valgrind on your code to detect any hidden memory access problems, or switch to smart pointers.
I am working on code which implements a Multilevel Feedback Queue Scheduler. There is something not clear in a part of the code:
void Scheduler_MFQS :: fill_queue(int clk) {
list<Process>::iterator itr;
for(itr = processes.begin(); itr != processes.end(); itr++) {
if((itr -> has_arrived(clk)) && (!queues[0].contains(*itr))) {
Process tmp (*itr);
queues[0].add_process(tmp);
remove(processes.begin(), processes.end(), *itr);
}
}
}
What this basically does is just put the process into the base queues under some condition. But i don't know what Process tmp (*itr); means? However, it compiles legally. Does that mean create a Process object called tmp? But what is the next, iterator (*itr) mean in c++?
"Process tmp (*itr);" mean?
It calls Process(const Process& &) copy constructor to create tmp object;
what is the next, iterator (*itr) mean in c++?
itr is std::list::iterator type, it's a pointer to current list node. *itr is getting the content of itr, which is a Process.
Your code can enhance a bit, demo as below:
// list<Process>::iterator itr; // move this into for loop, narrow variable scope and lifetime
/*auto if C++11*/
processes.unique(); // you actually only want unique processes from list
for(list<Process>::iterator itr = processes.begin(); itr != processes.end(); ++itr)
^^ call preincrement, faster
{
if((itr -> has_arrived(clk)) /*&& (!queues[0].contains(*itr))*/) {
^^ process list contains unique item only, no need to compare
//Process tmp (*itr); comment out this line, save one object copy
queues[0].add_process(*itr);
//remove(processes.begin(), processes.end(), *itr);
// You don't need to clear item in the loop
}
}
processes.clear(); // or swap with an empty list
// std::list<Process> p2;
// p2.swap(ps);
itr is the iterator which points to some container element(list in your case). When you use asterisk(*) on iterator you gain access to its content i.e. to the actual element of the list. In your case it is a Process object.
How can i loop thru a stl::List and store the value of one of the objects for use later in the function?
Particle *closestParticle;
for(list<Particle>::iterator p1 = mParticles.begin(); p1 != mParticles.end(); ++p1 )
{
// Extra stuff removed
closestParticle = p1; // fails to compile (edit from comments)
}
Either
Particle *closestParticle;
for(list<Particle>::iterator it=mParticles.begin(); it!=mParticles.end(); ++it)
{
// Extra stuff removed
closestParticle = &*it;
}
or
list<Particle>::iterator closestParticle;
for(list<Particle>::iterator it=mParticles.begin(); it!=mParticles.end(); ++it )
{
// Extra stuff removed
closestParticle = it;
}
or
inline list<Particle>::iterator findClosestParticle(list<Particle>& pl)
{
for(list<Particle>::iterator it=pl.begin(); it!=pl.end(); ++it )
{
// Extra stuff removed
return it;
}
return pl.end();
}
or
template< typename It >
inline It findClosestParticle(It begin, It end)
{
while(begin != end )
{
// Extra stuff removed
return begin;
++begin;
}
return end;
}
These are sorted in increasing personal preference. :)
For a list, the only way to invalidate an iterator is to erase it. So I suspect you're calling list.erase(p1) at some point in the loop. You need to make a copy of the iterator, move p1 back one, and then erase the copy.
EDIT: Oh wait, did you mean it doesn't compile? If so, see #sbi's answer. But you really need to word your question in a good way. What is your compile error? Or does it fail at run-time? In this case, however, I believe you mean a compile error.