Vishal Sharma
Vishal Sharma

Reputation: 1750

Unable to add multiple keys to map having struct as key

I'm trying to add multiple key-value pairs to a map having struct as both its key and value. Due to some reasons, only the first key-value pair that I add is being added to the map and the 2nd key-value pair couldn't be found.

#include<string>
#include<iostream>
#include<map>
using namespace std;

struct RJNodeAddress
{
            string m_ip;
            string m_port;
            bool operator<(const RJNodeAddress &l)const
            {
                    int l_size=l.m_ip.size();
                    int r_size=l.m_port.size();                                          
                    return (l_size < r_size);
            }
};

struct RJNodeDetails
{
        string m_NodeType;
        int        m_appId;
};

class RJWebsocket
{
        public:
            static map<RJNodeAddress,RJNodeDetails> m_Nodes;
};

map<RJNodeAddress,RJNodeDetails> RJWebsocket::m_Nodes;

int main()
{
        RJNodeAddress l_node1,l_node2;
        RJNodeDetails l_add1,l_add2;

        l_node1.m_ip="172.16.129.68";
        l_node1.m_port="8766";
        l_node2.m_ip="172.16.128.130";
        l_node2.m_port="8768";

        l_add1.m_appId=0;
        l_add1.m_NodeType="CM";

        l_add1.m_appId=1;
        l_add1.m_NodeType="CM";

        RJWebsocket::m_Nodes.insert({l_node1,l_add1});
        RJWebsocket::m_Nodes.insert({l_node2,l_add2});

    for(const auto& j:RJWebsocket::m_Nodes)
    {
    cout<<endl<<"Iterating through RJWebsocket::m_Nodes"<<endl;
    cout<<endl<<"port: "<<j.first.m_port<<"  "<<"IP: "<<j.first.m_ip<<endl;
    }

}

The above code gives the following output:

Iterating through RJWebsocket::m_Nodes

port: 8766  IP: 172.16.129.68

Upvotes: 0

Views: 150

Answers (3)

lubgr
lubgr

Reputation: 38267

Your comparison operator does something weird, I'd suggest relying on the one provided by std::tuple like this:

#include <tuple>

bool operator<(const RJNodeAddress &l)const
{
    return std::tie(m_ip, m_port) < std::tie(l.m_ip, l.m_port);
}

This way, the check for equivalence in std::map::insert can correctly be performed and your snippet inside the main function indeed constructs two entries. The output on my machine then is

Iterating through RJWebsocket::m_Nodes

port: 8768  IP: 172.16.128.130

Iterating through RJWebsocket::m_Nodes

port: 8766  IP: 172.16.129.68

Note that as @DanielLangr pointed out in the comments, RJNodeAddress could also directly be a type alias for std::pair<std::string, std::string> or std::tuple<std::string, std::string>, which provides a lexicographical operator < out of the box and falls back to std::string::operator <. This saves you from manually specifying the comparison, but has two drawbacks: it doesn't provide the expressiveness of naming the struct and its data members, and its lexicographical comparison might not be what you intended in the first place.

Upvotes: 5

Motti
Motti

Reputation: 114705

Your operator< is supposed to compare between two different RJNodeAddress, you seem to be only looking at the operand (l). Try something like this:

        bool operator<(const RJNodeAddress &l)const
        {
            if (m_ip == l.m_ip)
                return m_port < l.m_port;
            return m_ip < l.m_ip;
        }

Upvotes: 2

Jarod42
Jarod42

Reputation: 217275

Your operator doesn't respect compare requirement

bool operator<(const RJNodeAddress &l)const
{
        int l_size=l.m_ip.size();
        int r_size=l.m_port.size();                                          
        return (l_size < r_size); // Wrong
}

You probably want something like:

bool operator<(const RJNodeAddress &rhs)const
{
    auto as_tuple = [](const RJNodeAddress & a){std::tie(a.m_ip, a.m_port); };
    return as_tuple(*this) < as_tuple(rhs);
}

Upvotes: 2

Related Questions