[Dev] Pruning elements from sorted associative sequence containers (set, multiset, map, multimap)

M. Mueller (bhu5nji) dev@trilug.org
Tue, 12 Feb 2002 06:21:18 -0500


By trial and error and head banging I learned that pruning elements from 
sequence containers needs extra care.

My application (as before) is to keep transaction state information in a map 
where the key is the transaction ID and the entry is a struct with 
transaction related information, including a field called xState (transaction 
state).

It belongs to a system that is a single process daemon that manages multiple 
concurrent transactions from many clients.  It has three independent stages: 
transaction origination, transaction operation, transaction completion.

The transaction map is filled by origination and emptied by completion.  To 
operations, the map is a todo list.  The rate of growth, both positive and 
negative, in origination and completion are independent from a design 
perspective.  I can have 1 origination and 2 completions in a single main 
loop iteration.

In the completion stage the map is examined, element-by-element, looking for 
xState == XS_COMPLETE.  When such an element is found, the element is 
removed, or pruned, from the map.  This is where the learning occurred.

What I learned was tested on a map but I suspect the lesson applies to all of 
the sorted associative sequence containers (set, multiset, map, multimap).

Flawed code

{
	map<uint32_t, t_xAct>::iterator i;
	for (i = xMap.begin(); i != xMap.end(); i++) // endless loop
	{
		if (i->second.xState == XS_COMPLETE)
		{
			// completion duties
			xMap.erase(i); // disturbs loop guard
		}
	}
}

Code that appears to work:

{
	map<uint32_t, t_xAct>::iterator i;
TOP:
	for (i = xMap.begin(); i != xMap.end(); i++)
	{
		if (i->second.xState == XS_COMPLETE)
		{
			// completion duties
			xMap.erase(i); // disturbs loop guard
			goto TOP: // start search over again
		}
	}
}

I am not concerned about the "goto".  I could work around not using it.  It 
provides a concise way to demonstrate what I learned.

What I learned is that a container size must remain constant when an iterator 
is being used in a loop guard condition.  If this rule cannot be followed, 
then the loop must be forcefully aborted.  This is a practical rule only.

The Musser 2nd Ed. book has no example AFAIK showing the potential flaw and a 
work around.  The only hint is in Section 21.1.5 Sorted Associative Container 
Requirements on page 326 where it states,

"Finally, an *iterator* of a sorted associative container is bidirectional. 
The *insert* operations do not affect the validity of the iterators and 
references to the container, and *erase* operations invalidate only iterators 
and references to the erased elements."

It appears that the reason that *erase* invalidates iterators is left as an 
exercise for the reader.  This reader could use a little help, however.

----------------------

What seems to be missing from my library is a book filled with non-trivial 
examples from mixed applications - IT, communications, control, math, etc.  I 
am going to make it a point to read every single Josutiss example.  Then I'm 
going to the local bookeries to have another look at what's available.

So far, STL has been a bit of a struggle.  First I had the *for_each* 
questions then the *a.erase(i)* difficulties.  I believe the struggle is  
worthwhile.

Thanks for all the help.

Mike M.