I implemented the Visitor pattern in C++ using a STL-like iterator for storing the Visitor's current position in the container. Now I would like to change the container while I iterate over it, and I'm especially interested in deleting items from the container, even the one I'm currently visiting.
Now obviously this will invalidate the Visitors internal iterator, because it was pointing to exactly this item. Currently, I store a list of all iterators in the container and update them, as soon as anything is added to or removed from the list. So in a way this is similar to the Observer pattern applied to the iterator (as Observer) and the list (as Observable).
Alternatively I considered having the visitor() methods return some hint to the Visitor about what happend to the current item and how to proceed iterating, but that doesn't sound like such a good idea either, because the visit() implementation shouldn't really care about finding the next item.
So, my question is: What's the best way to keep a visitor working, even when items are added to the container or removed from it.
Regards,
Florian
Update: There's one visitor running over the container, but inside of the visit() method any number of additional iterators might be used on the same container. I want the visitor to continue with the remaining items in the container, even after we returned from a call to visit() in which any one of the items in the container got deleted.
When mutating the container during traversal, iterators are hazardous, at best. It is safest to use an index and walk backwards.
I think your (first) implementation is pretty good if there are not so many iterators and delete operations. If this would be the case I would use a mark and sweep like algorithm like Eddy recommended. Also, I think the latter is easier and thus less error prone. Don't forget to skip nodes which are marked for deletion.
On the other hand, if there are cases besides 'delete' where your iterators need to be updated, stick to your current implementation.
In these cases, if copying my container is not expensive, I just copy it and iterate on the copy. The original container holds objects by shared_ptr while the copy just holds weak_ptr.
Related
I made a singly linked list and am creating an iterator for it but I am running into a problem when the element currently pointed to gets removed from the list. I have a class called list and within it, I have a nested class called iter. Currently iter's only field is a *currentNode pointer (it gets passed in a node in the constructor).
So how do I handle when the element that it is pointing to gets deleted. My first thought was that I should try to handle that in my list's remove() functions but I don't think it is possible to / don't know how to tell from the list's point of view whether the current element is being pointed to by the iterator (the iterator's currentNode pointer seems to not be in the scope of the class based on the compiler's errors). My second thought was to try to handle it from the iterator itself, but I can't figure out how to even begin to go about that.
I'm sorry if the answer is really simple. I couldn't find the answer anywhere online which makes me think that it might be but I just can't figure out how to handle this for the life of me. Thank you!
The list does not need to know anything about the iterator or its content. When the iterator removes its current node from the list, the iterator is invalidated. Even iterators in most standard STL container behave that same way. If you look at std::list, for instance, its erase(iterator) method returns a new iterator that represents the next node in the list following the node that was removed. You can do the same in your own remove() method. Otherwise, remove() would have to update the iterator to point at a different node, but that tends to go against how iterators typically operate.
You have 2 options.
If you dereference an iterator to an object that doesn't exist it is undefined behavior. The standard library does it and it is accepted. Also it is easy to implement since you don't need to handle that case.
Keep a std::shared_ptr to the element. That way the value does not get deleted as long as the iterator exists. You have to pay a performance penalty for doing this even if you do not use the feature. Additionally it can be perceived as hiding a bug.
I have a pointer p (not an iterator) to an item in a list. Can I then use p to delete (erase) the item from the list? Something like:
mylist.erase(p);
So far I have only been able to do this by iterating through the list until I reach an item at the location p, and then using the erase method, which seems very inefficient.
Nope, you'll have to use an iterator. I don't get why getting the pointer is easier than getting an iterator though...
A std::list is not associative so there's no way you can use a pointer as a key to simply delete a specific element directly.
The fact that you find yourself in this situation points rather to questionable design since you're correct that the only way to remove the item from the collection as it stands is by iterating over it completely (i.e. linear complexity)
The following may be worth considering:
If possible, you could change the list to a std::multiset (assuming there are duplicate items) which will make direct access more efficient.
If the design allows, change the item that you're pointing to to incorporate a 'deleted' flag (or use a template to provide this) allowing you to avoid deleting the object from the collection but quickly mark it as deleted. Drawback is that all your software will have to change to accommodate this convention.
If this is the only bit of linear searching and the collection is not big (<20 items say.) For the sake of expediency, just do the linear search as you've suggested but leave a big comment in the code indicating how you "completely get" how inefficient this is. You may find that this does not become a tangible issue in any case for a while, if ever.
I'm guessing that 3 is probably your best option. :)
This is not what I advice to do, but just to answer the question:
Read only if you are ready to go into forbidden world of undefined behavior and non-portability:
There is non-portable way to make an iterator from T* pointer to an element in a list<T>. You need to look into your std library list header file. For Gnu g++ it includes stl_list.h where std::list definition is. Most typically std::list<T> consists of nodes similar to this:
template <class T>
struct Node {
T item;
Node* prev;
Node* next;
};
Having pointer to Node<T>::item you can by using offsetof calculate this node pointer. Be aware that this Node template could be the private part of std::list so you must hack this - let say by defining identical struct template with different name. std::list<>::iterator is just wrapper over this node.
It cannot be done.
I have a similar problem in that I'm using epoll_wait and processing a list of events. The events structure only contains a union, of which the most obvious type to use is void * to indicate which data is relevant (including the file descriptor) that was found.
It seems really silly that std::list will not allow you to remove an element via a pointer since there is obviously a next and previous pointer.
I'm considering going back to using the Linux kernel LIST macros instead to get around this. The problem with too much abstraction is that you have to give up on interoperability and communication with lower level apis.
I'm working on a UI. The base class for a UI component is UILayout, and the entire UI is a tree of UILayout objects, with the root being a UILayout representing the entire screen. In order to contain this hierarchy, any given UILayout has a vector mChildren of boost::shared_ptr<UILayout>.
A UIManager object takes care of Updating the entire hierarchy of UILayouts. Each call to Update iterates over the vector mChildren, calling Update on each child recursively.
Because changing the shape of the vector would invalidate those iterators, adding and removing entries from mChildren is confined to the ResizeChildren method. When components need to be added or removed, they are added to one of two vectors, mChildrenPendingAddition and mChildrenPendingRemoval. Immediately before the Update loop, ResizeChildren is called, and mChildren is updated accordingly. (Please stop me if this is an asinine way of handling this particular problem.)
I'm getting an exception when I attempt to remove from mChildren all entries which are also contained in mChildrenPendingRemoval. From UILayout::ResizeChildren():
mChildren.erase(remove_if(mChildren.begin(), mChildren.end(),
IntersectsWithChildrenPendingRemoval(this)), mChildren.end());
IntersectsWithChildrenPendingRemoval's comparison function calls this->ChildrenPendingRemovalContains(HUILayout ly), which does the following:
return (find(mChildrenPendingRemoval.begin(), mChildrenPendingRemoval.end(),
ly) != mChildrenPendingRemoval.end());
That line sometimes fails the debug assertion vector iterators incompatible. There are plenty of existing questions on this error, but it seems like it normally indicates that two iterators from different containers are being compared. But here, that's clearly not the case, right? What else could cause this problem?
Relevant source code:
Class and predicate definition
Implementation of the offending methods
This is a plugin that I'm developing for a multi-threaded application. The fact that the problem crops up at very rare and random intervals leads me to believe it has something to do with the fact that the plugin is running in separate threads, but all of these methods are called from a single function, squarely in a single thread, and mChildren is not accessed or modified in any other thread.
Please stop me if this is an asinine way of handling this particular problem
Why don't you work on a copy of the collection, and swap it in at once:
std::list<X> copy(mChildren);
copy.insert(...);
copy.remove(...);
copy.insert(...);
// at once:
std::swap(copy, mChildren);
Further thoughts:
it is in general not very convenient to keep iterators into mutable containers around for any period of time
Since this is a container of smart-pointers... why don't you pass the smart-pointers themselves around if you needed to keep 'pointers' to elements? (Of course, that wouldn't enable iteration, but that is IMO not a very healthy desire anyway)
Just use indices instead of iterators.
Iterators are overrated. They are nothing more than general pointers. The best way to manage items in a vector was and is allways to use indices. If you want to use iterators, try std::list, in this case iterators won't be invalidated even if you add elements to the list or remove them.
I need to replace specific key values, while the rest of the value_type is left untouched. What I actually need to do, is copy the value, erase the entry and insert it with changed key value again. This is absolutely bad. I need to copy the whole value_type twice, and deallocate/allocate again.
Why the standard doesn't define methods like this:
// returns count of exchanged keys
size_type exchange_key(key_type const& x, key_type const& y);
// returns count of replaced keys
size_type replace_key(key_type const& old_key, key_type const& new_key);
Is there anything I'm missing?
I don't why it was not added in the first place, and i understand that it is too bad. I guess they just added what they felt was absolutely necessary.
I think i have read somewhere that Boost.MultiIndex provided this ability.
Associative containers are implemented in a way that does not allow to change the 'key' in an efficient manner. To make this explicit it does not provide convienence methods to replace a key. The associative container would also have to remove and insert again under the covers.
I think this is an abstraction problem. The standard doesn't say exactly how the containers are to be implemented, it only specifies the maximum complexity of some of the operations and leaves the rest to the implementation.
If the standard were to add a replace_key function, it would also have to specify that this should have a different complexity than the existing erase-insert combination. How can it do that without leaking implementation details? If the function isn't guaranteed to be faster on all implementations, it is pretty useless.
When you say that it would obviously be faster, you make assumptions about implementation details that the standard tries to avoid.
Now, you can, with .extract(key) (since C++17).
https://en.cppreference.com/w/cpp/container/map/extract
This is because changing a key could affect the structure of an associative containers. Notably, std::map, which is a typically Red-Black tree, the tree structure mostly will be changed once you modify a key (e.g., rotating sub trees). In some data structures, even such dynamic changes are disallowed. So, it is challenging to expose such operation as a standard in an associative container.
Regarding the overhead you concerned, once you have value_type as a pointer or reference, the overhead of deleting/inserting a pair isn't too bad.
Well, honestly behind the screens it would result into an insert and delete operation anyhow, with the sole difference that the value-part will not be copied. While this seems to be your biggest concern, unless your object is very heavy on copying, in a large container, the update operation to re-stabilize the ordered container will be heavier anyhow.
Now this would require some important changes however going further than the associative containers, the two most important I can see are:
The std::pair class needs an update, because you must be able to update the key without creating a new pair (as this would also copy the value object).
You need an update function that removes a pair from the tree, calls the new logic from 1., and reinserts it.
I think the main problem lies with the first one, as std::pair is at the moment actually a very simple wrapper, and your proposal would delete that principle and add some (unnecessary) complexity to it. Also note that call 2 does not actually add new functionality but wraps a system the developer could manage himself easily through references and the like. If they would add all this wrapping to std, it would become a really huge bloated piece of library.
If you want this principle, you should look for more complex libraries (probably boost has some). Or you should simply use a reference/shared_ptr as your value_type.
I'm new to c++ but have set my mind on a specific task that needs me to enable adding a specific chunk of code to be execute whenever any list item is attempted to be changed or read.
The resulting list should behave and look as much as as possible to std::list except for this small exception that would enable me to execute a specific tasks whenever this list is about to be read/written to.
From what i have found out so far, all i could think of is deriving a class from list::iterator and overloading it's operator* and operator= to implement these specific tasks.
Then i should derive a class from std::list and make it use my new iterator type by overloading begin() and end() methods. Or is there a better way of making it use a custom iterator?
That would handle the iterator access but I can see lists can even return pointers to it's members. I guess there is nothing i can do about them and will have to remove this feature from my new list class.
I would appreciate your oppinion on this subject.
Deriving from std::list is almost certainly not the answer. The collections in stl are simply not meant to be derived from and doing so will cause you problems down the road.
The classic example of why is the destructor problem. The destructors in stl collections are not virtual. This will break any logic you place in your derived class destructor if an object is deleted via a reference to the std::list. For example
std::list* pList = new YourNewListClass();
delet pList; // runs std::list::~list()
You'd also need to override much more than the methods on the iterator. It would require changing every method which can possibly mutate the collection.
A more stable approach would be to implement your own std::list style class which follows the standard stl container behavior. You could then include use this list in the places you wanted events without running into the problems with deriving from std::list.
Look at the way that std::deque implements it's functionality as an adaptor of another standard collection. This is the way to go -- use composition not inheritance and wrap the underlying collection to provide your new facilities. For bonus points template your implementation on the underlying collection. For many uses a std::vector will outperform a std::list and your additions should be able to work equally well with whichever of these the user chooses.
First things first..NEVER derive a class from STL containers as they are not meant to be derived. For starters their destructor is not virtual.
The easiest way would be contain a std::list in your own class and providing list like interfaces. In these list like interfaces you can provide any additional tasks you want to perform before/updating the list.
Also, take a look at this design pattern: Decorator
Take a look at boost::transform_iterator<>. It seems to be close to what you're looking for. It calls a functor whenever the operator*() function of the transform_iterator<> is called. The intended use is to transform the object the iterators points to, but there's nothing that says the functor can't do something else and simply return the original value of the pointed to object.
Even if it's not quite what you want, it will probably provide ideas to how you might approach your problem.