Search code examples
c++reverse-iterator

C++ rbegin modify address of reverse_iterator


I have a strange problem in c++ with this code:

mutex_type  list_mutex;
typedef list<char*> RQueue;
RQueue rQueue;
RQueue::reverse_iterator  rstart, rend, last;

  1  while(true) {
  2      LockMutex(list_mutex);
  3      rstart = rQueue.rbegin();
  4      rend   = rQueue.rend();
  5      while( (rstart != rend) && (rstart != last) ) {
  6           print *rstart;
  7      }
  8      last = rQueue.rbegin(); 
  9      UnlockMutex(list_mutex);
  10  }
  • rQueue is a queue in which I iterate in reverse order
  • rQueue can receive messages at any time
  • I added the iterator last in order to avoid reworking with a receive message at line 6
  • At line 8, I retain the position from where I printed the messages and I want to only print the messages that are newer than the last message.

    My problem: When a iteration is finished and new messages are added in the queue, the value of iterator last is changed, becoming the same as the value of the iterator rstart, therefore the new arrived messages are not printed at line 6.

I don't know why last = rQueue.rbegin() modifies its value when receiving new elements after unlocking the queue.

Thanks.


Solution

  • If you set an iterator to rbegin(), it will always keep pointing to the last element of the list. If you add another element at the back, the iterator will point still point to the last element (which now is the new one). It will not change, it just keeps pointing to the end.

    I did this test:

    list<const char *> my_list;
    my_list.push_back("msg 1");
    
    list<const char*>::reverse_iterator it = my_list.rbegin();
    
    cout << "Iterator is " << *it << endl;
    
    my_list.push_back("msg 2");
    my_list.push_back("msg 3");
    my_list.push_back("msg 4");
    
    cout << "Iterator is " << *it << endl;
    

    This program gives the output:

    Iterator is msg 1
    Iterator is msg 4
    

    I have this other solution you might use which does not use a reverse iterator. Instead, the addMessage()-function updates read_pos to the newest message. If read_pos does not point to the end, it is not changed either. This allows printMessage() to print all messages which where added since last time it ran.

    Notice that I have only tested this without the locking.

    mutex_type  list_mutex;
    typedef list<const char*> RQueue;
    RQueue rQueue;
    
    RQueue::iterator read_pos;
    
    void addMessage(const char *message) {
        LockMutex(list_mutex);
    
        rQueue.push_back(message);
    
        if (rQueue.size() == 1) {
            read_pos = rQueue.begin();
        }
        else if (read_pos == rQueue.end()) {
            read_pos--;
        }
    
        UnlockMutex(list_mutex);
    }
    
    void printMessage() {
      RQueue::iterator prev_pos;
    
      while (true) {
        LockMutex(list_mutex);
    
        if (rQueue.size() == 0) {
              UnlockMutex(list_mutex);
              continue;
        }
    
        RQueue::iterator end = rQueue.end();
        while (read_pos != end) {
            cout << *read_pos << endl;
            read_pos++;
        }
    
        UnlockMutex(list_mutex);
      }
    }