Thread: Erased element from std::set reappears.

  1. #1
    Registered User
    Join Date
    Mar 2012
    Posts
    6

    Erased element from std::set reappears.

    Hello. I am trying to make something like an event or message system. There are Listeners, Messages and a Postman. The Listeners register themselves with the Postman to receive certain types of messages. You can send Messages through the Postman to all Listeners registered for that Message type.

    The Postman keeps track of Listeners by mapping Message type to a std::set of Listeners. When a Message is posted, the receiveMessage function of all Listeners in the appropriate set will be called.

    The problem is with unregistering. It is supposed to remove a Listener from all sets. And from checking size of the set before and after erase that seems to work. But for some reason, the next time a Message is posted, the unregistered Listener still receives the Message.

    It is like I erase from a copy of the set or something? But I can't see the error.

    Code:
    #include <iostream>
    #include <set>
    #include <unordered_map>
    
    class Message {
    public:
        enum Type { MESSAGE0, MESSAGE1, MESSAGE2, MESSAGE3 } type;
        Message(Type t) : type(t) {}
    };
    
    class Listener {
    public:
        Listener(int id) : id(id) {}
        void receiveMessage(Message m) {
            std::cout << "  Listener " << id << " received a message of type " << m.type << std::endl;
        }
    private:
        int id;
    };
    
    class Postman {
    public:
        Postman() {
            listeners = std::unordered_map<Message::Type, std::set<Listener*>, std::hash<int> >();
        }
        void postMessage(Message message) {
            std::cout << "Someone is sending a message of type " << message.type << std::endl;
            auto it = listeners.find(message.type);
            if(listeners.end() != it) {
                std::cout << "The set for message type " << message.type << " has size " << it->second.size() << "." << std::endl;
                for(auto listener : it->second)
                    listener->receiveMessage(message);
            }
        }
        void registerListener(Listener *l, Message::Type type)
        {   
            auto it = listeners.find(type);
            if(listeners.end() == it) {
                std::pair<Message::Type, std::set<Listener*> > pair (type, std::set<Listener*>());
                auto result = listeners.insert(pair);
                if(result.second) {
                    it = result.first;
                }
                else {
                    std::cout << "Insertion failed." << std::endl;
                    return;
                }
            }
            listeners.at(type).insert(l);
        }
        void unregisterListener(Listener *l) {
            for(auto it : listeners) {
                std::cout << "The set for message type " << it.first << " has size " << it.second.size() << "." << std::endl;
                int i = it.second.erase(l);
                std::cout << "  I erased " << i << " elements from the set." << std::endl;
                std::cout << "  The set now has size " << it.second.size() << "." << std::endl;
            }
        }
    private:
        std::unordered_map<Message::Type, std::set<Listener*>, std::hash<int> > listeners;
    };
    
    int main(int argc, char** argv) {
        auto pm = Postman();
        auto l1 = Listener(1);
        auto l2 = Listener(2);
        pm.registerListener(&l1, Message::Type::MESSAGE1);
        pm.registerListener(&l1, Message::Type::MESSAGE3);
        pm.registerListener(&l2, Message::Type::MESSAGE2);
        pm.registerListener(&l2, Message::Type::MESSAGE3);
        std::cout << std::endl;
        pm.postMessage(Message(Message::Type::MESSAGE1));
        pm.postMessage(Message(Message::Type::MESSAGE2));
        pm.postMessage(Message(Message::Type::MESSAGE3));
        std::cout << std::endl;
        pm.unregisterListener(&l1);
        std::cout << std::endl;
        pm.postMessage(Message(Message::Type::MESSAGE1));
        pm.postMessage(Message(Message::Type::MESSAGE2));
        pm.postMessage(Message(Message::Type::MESSAGE3));
    }
    In main I create two Listeners. One subscribes to type 1 and 3, the other to 2 and 3. Then I send a message of each type.

    Then one of the Listeners unregisters and the same three messages are sent. The unregistered Listener still receives them, and the size of the sets have gone up again.

    The exact output from the above program is
    Code:
    Someone is sending a message of type 1
    The set for message type 1 has size 1.
      Listener 1 received a message of type 1
    Someone is sending a message of type 2
    The set for message type 2 has size 1.
      Listener 2 received a message of type 2
    Someone is sending a message of type 3
    The set for message type 3 has size 2.
      Listener 2 received a message of type 3
      Listener 1 received a message of type 3
    
    The set for message type 2 has size 1.
      I erased 0 elements from the set.
      The set now has size 1.
    The set for message type 3 has size 2.
      I erased 1 elements from the set.
      The set now has size 1.
    The set for message type 1 has size 1.
      I erased 1 elements from the set.
      The set now has size 0.
    
    Someone is sending a message of type 1
    The set for message type 1 has size 1.
      Listener 1 received a message of type 1
    Someone is sending a message of type 2
    The set for message type 2 has size 1.
      Listener 2 received a message of type 2
    Someone is sending a message of type 3
    The set for message type 3 has size 2.
      Listener 2 received a message of type 3
      Listener 1 received a message of type 3
    Any help would be appreciated.

  2. #2
    Guest
    Guest
    Code:
    unregisterListener(Listener *l) {
        for(auto it : listeners) { // try an auto& (reference) here, your suspicion about handling a copy is probably correct
            std::cout << "The set for message type " << it.first << " has size " << it.second.size() << "." << std::endl;
            int i = it.second.erase(l);
            std::cout << "  I erased " << i << " elements from the set." << std::endl;
            std::cout << "  The set now has size " << it.second.size() << "." << std::endl;
        }
    }

  3. #3
    Registered User
    Join Date
    Mar 2012
    Posts
    6
    Quote Originally Posted by Guest View Post
    ...try an auto& (reference) here
    That did it. Thanks!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Insert element after the last element in array in c
    By amaturequestion in forum C Programming
    Replies: 3
    Last Post: 04-09-2015, 08:29 AM
  2. vector remove element of element
    By Ducky in forum C++ Programming
    Replies: 6
    Last Post: 09-12-2010, 03:24 PM
  3. how do I stop text from being erased
    By rakan in forum Windows Programming
    Replies: 2
    Last Post: 02-20-2008, 12:00 AM
  4. erased pointer
    By hedwin in forum C++ Programming
    Replies: 2
    Last Post: 11-06-2007, 06:21 AM
  5. Grabbing the return value before it's erased.
    By hardi in forum C++ Programming
    Replies: 18
    Last Post: 12-20-2006, 09:35 AM