map iterator not incrementable - c++

When I try to run my program I get 'map/set iterator not incrementable' error. I read that it happens when you invalidate iterator, but I'm only editing objects pointed by iterator. Is this normal behaviour? What can I do about it?
If I delete this line then there is no error:
iterator->second.neighbour[direction] = &(find_iter->second);
Full code:
void Gomoku::addUniqueMap( const map_Map& map_to_add )
{
this->unique_maps.push_front( map_to_add );
std::list<map_Map>::iterator map_iter = this->unique_maps.begin();
map_Iterator iterator = map_iter->begin();
for( ; iterator != map_iter->end(); ++iterator )
{
for( int i = 1, direction = 0; i > -2; --i )
{
for( int j = -1; j < 2; ++j )
{
if( iterator->second.neighbour[direction] == nullptr )
{
++direction;
continue;
}
if( i == 0 && j == 0 )
continue;
COORD pos( iterator->first.x + j, iterator->first.y + i );
wrap( pos, this->map_size );
map_Iterator find_iter = map_iter->find( pos );
if( find_iter != map_iter->end() )
{
iterator->second.neighbour[direction] = &(find_iter->second);
}
++direction;
}
}
}
}
'map_Map' - std::map<COORD, Pawn>
'map_Iterator' - std::map<COORD, Pawn>::iterator
'Pawn' -
struct Pawn
{
Pawn( Player player );
Player player;
const Pawn* neighbour[8];
};

The cause of the error was that I was accessing and modifying neighbour[8], so I was modifying element of out array range and possibly it has done something to the iterator.
In Debug version I got map/set iterator not incrementable error while in Release version iterator couldn't be incremented and it caused program to loop infinitely.

Related

Why doesn't map insert?

Why doesn't UpdateLapMap insert a UapMap when the lap is not found?
typedef std::map<int, int> UapMap; // map of uap counters
typedef std::map<int, UapMap> LapMap; // map of UapMaps
LapMap m_LapMap;
void MyClass::UpdateLapMap( int lap, int * uaps, size_t n_uaps )
{
std::map<int, UapMap>::iterator itLap = m_LapMap.find( lap );
if ( itLap == m_LapMap.end( ) )
{
printf( "not found - insert new lap %d\n", lap );
for ( size_t i = 0; i < n_uaps; i++ ) itLap->second[ uaps[ i ] ] = 1; // initial count
}
else
{
/// insert and/or increment uap counters
}
}
You are using itLap->second when itLap == m_LapMap.end( ).
std::map::end() returns a placeholder element and attempting to access it invokes undefined behavior.
UpdateLapMap doesn't insert a UapMap because there are no code to insert a UapMap, so you should add that.
for example:
if ( itLap == m_LapMap.end( ) )
{
printf( "not found - insert new lap %d\n", lap );
itLap = m_LapMap.insert( LapMap::value_type( lap, UapMap() ) ).first; // add this line
for ( size_t i = 0; i < n_uaps; i++ ) itLap->second[ uaps[ i ] ] = 1; // initial count
}
std::map::insert() used here returns a pair of an iterator pointing at the inserted element and a boolean value indicating whether insertion is done or the key has already existed, so the iterator is taken via .first.

I need to remove vector elements based on timeval

I have a queue of audio elements for each frame of a movie. I want to remove them from last seen to first seen up to a certain max number of elements. I use timeval to determine the time value. I have problems with the iterators due to calling erase on the elements.
I tried creating a std::multimap to store all the iterators based on the timeval key. Then I stored all the iterators on a vector based on max_frames to remove. I then sorted the vector of iterators from greater to less. Then erased all iterators.
// C++
void CMedia::timed_limit_audio_store(const int64_t frame)
{
unsigned max_frames = max_audio_frames();
struct customMore {
inline bool operator()( const timeval& a,
const timeval& b ) const
{
return (((a).tv_sec == (b).tv_sec) ?
((a).tv_usec > (b).tv_usec) :
(a).tv_sec > (b).tv_sec)));
}
};
typedef std::multimap< timeval, audio_cache_t::iterator,
customMore > TimedSeqMap;
TimedSeqMap tmp;
{
audio_cache_t::iterator it = _audio.begin();
audio_cache_t::iterator end = _audio.end();
for ( ; it != end; ++it )
{
if ( (*it)->frame() - max_frames < frame )
tmp.insert( std::make_pair( (*it)->ptime(), it ) );
}
}
unsigned count = 0;
TimedSeqMap::iterator it = tmp.begin();
typedef std::vector< audio_cache_t::iterator > IteratorList;
IteratorList iters;
for ( ; it != tmp.end(); ++it )
{
++count;
if ( count > max_frames )
{
// Store this iterator to remove it later
iters.push_back( it->second );
}
}
IteratorList::iterator i = iters.begin();
IteratorList::iterator e = iters.end();
// We sort the iterators from bigger to smaller to avoid erase
// trashing our iterator. However, this is not working properly.
std::sort( i, e, std::greater<audio_cache_t::iterator>() );
i = iters.begin();
e = iters.end();
for ( ; i != e; ++i )
{
_audio.erase( *i );
}
}
The expected result would be that the vector's elements are removed based on the timeval of the audio elements. The actual errors are memory trashing and sometimes crashes.

Removing one element of a vector while in a loop

for (Shape *i : shapes) {
for (Shape *j : shapes) {
if (i != j) {
if (check(i,j)){
shapes.erase(remove(shapes.begin(), shapes.end(), i), shapes.end());
this causes an error because it's going to carry on iterating even though i does not exist, my question is how do I cleanly do this? currently I get an error "vector iterator not incrementable"
Can i just exit the second loop and continue in the first one?
You cannot erase elements from a vector when you are iterating it by for range loop, as internally it uses iterators that would be invalidated. This should work:
auto end = shapes.end();
for( auto it = shapes.begin(); it != end; ++it ) {
end = shapes.erase( std::remove_if( std::next( it ), shapes.end(), [it]( Shape *s ) {
return check( *it, s );
}, shapes.end() );
}
Note this code is slightly more effective than yours but it assumes that check( s1, s2 ) == check( s2, s1 ), if you can change your check() function to do strict ordering comparison, rather than equivalence, then you would be able to use std::sort and std::unique which are even more effective.
You can't modify the positioning of your shapes elements while using ranged-based for loops. The range loop uses iterators internally, and erasing vector elements invalidates existing iterators.
Try something more like this instead:
auto iter = shapes.begin();
auto end = shapes.end();
while (iter != end) {
auto iter2 = shapes.begin();
bool erased = false;
while (iter2 != end) {
if ((iter != iter2) && check(*iter, *iter2)) {
iter = shapes.erase(iter);
end = shapes.end();
erased = true;
break;
}
++iter2;
}
if (!erased)
++iter;
}
Alternatively, maybe something more like this would also work:
shapes.erase(
std::remove_if(shapes.begin(), shapes.end(),
[shapes&](Shape *i) {
for (Shape *j : shapes) {
if ((i != j) && check(i, j)) {
return true;
}
}
return false;
}
),
shapes.end()
);
You cannot use a range-for loop in this case. Instead use a standard loop with iterators:
for (auto iter = shapes.begin(); iter != shapes.end(); iter++)

c++ map erase function not working properly with iterator

I am removing the elements from map by using erase in the following way but it is not working properly. Why? It is not removing all.
float nw_cut=80.0;
for(it=nw_tot1.begin();it!=nw_tot1.end();it++)
{
float f=(float) ((float) it->second/lines)*100.0;
if ( f < nw_cut )
{
nw_tot1.erase(it);
}
}
From std::map::erase():
References and iterators to the erased elements are invalidated. Other references and iterators are not affected.
If erase(it) is called then it is invalidated, and it is then used by the for loop causing undefined behaviour. Store the return value of erase(), which returns an iterator to the next element after the erased element (since c++11), and only increment if erase() was not called:
for(it = nw_tot1.begin(); it != nw_tot1.end();)
{
float f=(float) ((float) it->second/lines)*100.0;
if ( f < nw_cut ) it = nw_tot1.erase(it);
else ++it;
}
In c++03 (and c++11 also) this could be accomplished with:
for(it = nw_tot1.begin(); it != nw_tot1.end();)
{
float f=(float) ((float) it->second/lines)*100.0;
if ( f < nw_cut ) nw_tot1.erase(it++);
else ++it;
}
You should do something like this:
float nw_cut=80.0;
for(it=nw_tot1.begin();it!=nw_tot1.end();)
{
float f=(float) ((float) it->second/lines)*100.0;
it_temp = it; //store it in temp variable because reference is invalidated when you use it in erase.
++it;
if ( f < nw_cut ) {
nw_tot1.erase(it_temp);
}
}
for(it = nw_tot1.begin(); it != nw_tot1.end();)
{
float f=(float) ((float) it->second/lines)*100.0;
if ( f < nw_cut ) it = nw_tot1.erase(it);
else ++it;
}

stdvector inserting and erasing problem

I need to control the amount to touch points in my application, for this I am using a vector container and my basic setup is this:
//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
isTouching = true;
touchPoints.push_back( ofVec2f( touch.x, touch.y ) );
}
//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
for ( int i = 0; i < touchPoints.size(); i++ ) {
if ( touch.id == i ) {
touchPoints[i] = ofVec2f( touch.x, touch.y );
}
}
}
//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){
isTouching = false;
int i = 0;
for ( vector<ofVec2f>::iterator iter = touchPoints.begin(); iter != touchPoints.end(); ) {
//int i = std::distance( touchPoints.begin(), iter );
cout << "i: " << i << endl;
if ( touch.id == i ) {
iter = touchPoints.erase( iter );
}
i++;
++iter;
}
}
But when I move up a finger the app freezes, so there most be something wrong in the touchUp(), any ideas?
Many things: First off, you cannot modify (erase/insert) a container and expect iterators to remain valid!
Let's see. I want to modify touchMove as well:
void testApp::touchMoved(const ofTouchEventArgs & touch)
{
if (touch.id < touchPoints.size())
touchPoints[touch.id] = ofVec2f(touch.x, touch.y);
}
Next, the big one:
void testApp::touchUp(const ofTouchEventArgs & touch)
{
if (touch.id < touchPoints.size())
touchPoints.erase(touchPoints.begin() + touch.id);
}
Basically touch.id is just the index in the vector, so we can use that directly. To erase an element from the middle, we just call erase on the corresponding iterator. Since vector has random access iterators, we can say begin() + touch.id in constant time.
Update: Actually I think your code is broken: After you erase an element from the vector, the other elements move up, so you will lose the association between touch.id and the container element! What's needed is, you guessed it, an associative container:
struct testApp
{
std::map<int, ofVec2f> touchPoints;
bool isTouching;
void touchDown(const ofTouchEventArgs & touch)
{
isTouching = true;
touchPoints[touch.id] = ofVec2f(touch.x, touch.y);
}
void touchMoved(const ofTouchEventArgs & touch)
{
touchPoints[touch.id] = ofVec2f(touch.x, touch.y);
}
void touchUp(const ofTouchEventArgs & touch)
{
isTouching = false;
touchPoints.erase(touch.id);
}
};
If you've done iter=touchPoints.erase(iter) you shouldn't then do ++iter; you've already moved on to the next item.