0

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.

  • A classic mistake to make with `erase` is to not assign your iterator to the result vs incrementing the iterator. This is what is happening in `_audio.erase(*i)` – AndyG Aug 15 '19 at 19:39
  • `i = iters.begin(); e = iters.end(); for ( ; i != e; ++i ) { _audio.erase( *i ); }` is just going to erase the entire vector. To do that, just use `_audio.clear()`. Otherwise, if you want to remove select elements, see the [erase-remove idom](https://stackoverflow.com/questions/347441/erasing-elements-from-a-vector) – NathanOliver Aug 15 '19 at 20:00
  • AndyG, I cannot assign the result of _audio.erase(*i) to anything as iters is a vector of iterators. It is not a simple loop with the common i = _audio.erase(*i); – user2517097 Aug 15 '19 at 21:14
  • NathanOliver. It does not erase the whole vector, as _audio is a separate vector from iters. Iters has iterators pointing to _audio. – user2517097 Aug 15 '19 at 21:15
  • Never mind me. I was able to use the erase(remove_if) idiom with a custom comparison operator. Now all is fine. – user2517097 Aug 16 '19 at 03:01

0 Answers0