Andre Ahmed
Andre Ahmed

Reputation: 1891

erase iterator from list in debug and release mode?

I have a strange problem here. I'm trying to erase an iterator from a particle system (std::list). I erase an iterator when the particles goes out of screen. When I checked the size of the particles in debug mode, I found that they are the double compared to release mode, I don't know why.

Kindly find the following code

 void ParticleManager::update(std::vector<ci::Vec2f> masses)
    {
        int targetDifference = masses.size() - m_Targets.size();

        if (masses.size() == 1 && targetDifference == 1)
        {
            addTarget();
        }
        else if (targetDifference > 1)
        {
            addTarget();
        } 
        else if (targetDifference < 0)
        {
            deleteTarget();
        }

        Vec2f currVec, offset;
        float fCurrLengthSquared;
        bool bAllTargetsActive = true;

        std::list<Particle>::iterator p = m_Particles.begin();
        while( p != m_Particles.end() ) 
        {
            p->update();

            float fMinSquaredLength = 0.0f;

            // influence of the masses
            for( int i = 0; i < (int) masses.size(); ++i )
            {
                currVec = masses[i] - p->m_Position;
                fCurrLengthSquared = currVec.lengthSquared();

                if (fCurrLengthSquared < ParamMgr.m_fForceMinDist * ParamMgr.m_fForceMinDist)
                {
                    fCurrLengthSquared = ParamMgr.m_fForceMinDist * ParamMgr.m_fForceMinDist;
                }

                if(fCurrLengthSquared < ParamMgr.m_fForceMaxDist * ParamMgr.m_fForceMaxDist)
                {
                    offset = currVec.normalized() / (fCurrLengthSquared / 500.0f);  // 1000.0f      
                    p->m_Direction += offset * ((float) TimerMgr.getDeltaTime() * ParamMgr.m_fGravity * ParamMgr.m_fGravity );
                }

                /*if( i == 0 )
                    fMinSquaredLength = fCurrLengthSquared;

                if( fCurrLengthSquared < fMinSquaredLength )
                    fMinSquaredLength = fCurrLengthSquared;*/
            }

            if( masses.size() > 0 )
            {
                float fSquareColorRadius = ParamMgr.m_fColorRadius * ParamMgr.m_fColorRadius;
                if( fMinSquaredLength > fSquareColorRadius )
                fMinSquaredLength = fSquareColorRadius;

                float fIntensity = 1.0f - (fMinSquaredLength / fSquareColorRadius) * 0.9f;
                //p->m_Color = ci::Color(0.0f, fIntensity, 0.0f);
                //p->m_Color = ci::Color(0.0f, 1.0f, 0.0f);
            }

            p->m_fAge += (float) TimerMgr.getDeltaTime();

            // outside the window
            if( p->m_Position.x < 0.0f - m_fCollisionOffsetX || p->m_Position.x > getWindowWidth() + m_fCollisionOffsetX ||
                p->m_Position.y < 0.0f - m_fCollisionOffsetY || p->m_Position.y > getWindowHeight() + m_fCollisionOffsetY )
            {
                p->m_bIsDead = true;
            }
            else
            { 
                // check targets
                for( std::list<Target>::iterator t = m_Targets.begin(); t != m_Targets.end(); t++ )
                {
                    if( t->checkParticle( p->m_Position ) )
                    {
                        p->m_ColorChange = Color( CM_HSV, t->m_fHue, t->m_fSat * 0.9f, 1.0f );
                        if( p->checkTarget( *t ) )
                        {
                            t->addParticleHit();
                        }
                    }
                    else
                    {
                        if( t != m_Targets.end() )
                        {
                            p->m_Targets.remove( *t );
                            //std::cout << "Try to erase: " << (*t).m_Position << " from target list." << std::endl;
                        }
                    }
                }
            }   

            if( p->m_bIsDead )
            {
                p = m_Particles.erase(p);

            }   
                p++;
}

Upvotes: 0

Views: 199

Answers (1)

Edward Loper
Edward Loper

Reputation: 15944

Two points:

  1. You should use p->m_Targets.erase(t); rather than p->m_Targets.remove(*t). In particular, since you're using remove(), which takes a value, the std::list class must scan through the entire list to find the value you specify.

  2. Removing an element from a list invalidates any iterators that point at that element. So your t iterator is getting invalidated. Using an invalid iterator has undefined behavior. Luckily, it's (relatively) easy to solve. Try something like this:

    std::list<Target>::iterator t = m_Targets.begin(); 
    while (t != m_Targets.end())
    {
        if( t->checkParticle( p->m_Position ) )
        {
            p->m_ColorChange = Color( CM_HSV, t->m_fHue, t->m_fSat * 0.9f, 1.0f );
            if( p->checkTarget( *t ) )
            {
                t->addParticleHit();
            }
            ++t;
        }
        else
        {
            t = p->m_Targets.erase(t);
        }
    }
    

Note that we only do "++t" if we don't remove an element (and not on every iteration through the loop, as you did with your for loop).

There's a similar bug in your outer loop. Replace this:

        if( p->m_bIsDead )
        {
            p = m_Particles.erase(p);
        }   
        p++;

with this:

        if( p->m_bIsDead )
            p = m_Particles.erase(p);
        else
            ++p;

Upvotes: 2

Related Questions