1,105,633 Community Members

Hash_map not working with struct values

Member Avatar
arthurav
Light Poster
38 posts since Jun 2010
Reputation Points: 0 [?]
Q&As Helped to Solve: 0 [?]
Skill Endorsements: 0 [?]
 
0
 

I want to use hash_map with struct values. The code is the following:

#include<stdio.h>
#include<string.h>
#include<hash_map>

using namespace std;
using namespace __gnu_cxx;

struct row {
    int number;
    char type;
};

struct eqstr {

    bool operator()(const char* s1, const char* s2) const {
        return (strcmp(s1,s2));
    }
};


int main() {

    hash_map<const char*, struct row, hash <const char *>, eqstr> columns;
    struct row r1,r2;
    r1.number=1;
    r2.number=2;
    r1.type='a';
    r2.type='b';
    columns["first"]=r1;
    columns["second"]=r2;
    printf("%i %c\n",columns["first"].number,columns["first"].type);
    printf("Columns hash_map size %i\n",columns.size());
    hash_map<const char*, struct row, hash <const char *>, eqstr>::iterator it;
    it=columns.begin();
    for(int i=0;i<columns.size();i++)
    {
        printf("%s %i %c\n",it->first,it->second.number,it->second.type);
        it.operator ++();
    }
}

The first printf says columns["first"].number is 0 and columns["first"].char=(null)
It says that the size of the hash_map is 4 (though it has only 2 elements).

The iterator shows me the following ellements:
key row.number row.type
first 0
first 0
first 1 a
second 2 b

What's going on? Thank you.

Member Avatar
thekashyap
Practically a Posting Shark
809 posts since Feb 2007
Reputation Points: 193 [?]
Q&As Helped to Solve: 77 [?]
Skill Endorsements: 0 [?]
 
0
 

I don't have hash_map & hash so I replaced hash_map with map and removed hash.
It seems to work though there seems to be a small problem with printf().

What I suggest (to debug this further) is to create default and copy c'tor for your struct (or change it to a class, while you're at it) and put some messages. (so you know exactly what's happening) Here is my code with output.

#include<stdio.h>
#include<string.h>
#include<map>

using namespace std;
using namespace __gnu_cxx;

struct row {
    int number;
    char type;
    row() {
    }
    row(const row& r) {
        number = r.number;
        type = r.type;
        printf("\ninside row(const row&): %i, -%c-", r.number, r.type);
    }
};

struct eqstr {

    bool operator()(const char* s1, const char* s2) const {
        printf("\ninside operator(): -%s-, -%s-", s1, s2);
        return (strcmp(s1,s2));
    }
};


int main() {

    map<const char*, struct row, eqstr> columns;
    struct row r1,r2;
    r1.number=1;
    r2.number=2;
    r1.type='a';
    r2.type='b';
    printf("\nbefore first insert");
    columns["first"]=r1;
    printf("\nbefore second insert");
    columns["second"]=r2;
    printf("\nbefore printf");
    printf("\n%i %c\n",columns["first"].number,columns["first"].type);
    printf("\nColumns map size %i",columns.size());
    map<const char*, struct row, eqstr>::iterator it;
    it=columns.begin();
    printf("\nbefore loop");
    for (int i=0;i<columns.size();i++) {
        printf("\n%s %i %c",it->first,it->second.number,it->second.type);
        it.operator ++();
    }
    printf("\nafter loop");

    return 0;
}

Output:
---------------------------------
before first insert
inside row(const row&): 1, -á-
inside row(const row&): 1, -á-
before second insert
inside operator(): -first-, -second-
inside row(const row&): 1, -á-
inside operator(): -first-, -second-
inside row(const row&): 1, -á-
inside operator(): -second-, -first-
before printf
inside operator(): -first-, -first-
inside operator(): -second-, -first-
inside operator(): -first-, -first-
inside operator(): -first-, -first-
inside operator(): -second-, -first-
inside operator(): -first-, -first-
1 a

Columns map size 2
before loop
second 2 b
first 1 a
after loop
---------------------------------

PS:
A more dignified loop (compared to it.operator ++(); ) would be:

map<const char*, struct row, eqstr>::iterator it = columns.begin();
    for ( ; it != columns.end(); it++) {
        printf("\n%s %i %c",it->first,it->second.number,it->second.type);
    }

And your comparator should look like this:

return (strcmp(s1,s2) == 0);
//instead of
return (strcmp(s1,s2));
Member Avatar
mike_2000_17
21st Century Viking
4,084 posts since Jul 2010
Reputation Points: 2,253 [?]
Q&As Helped to Solve: 800 [?]
Skill Endorsements: 73 [?]
Moderator
Featured
Sponsor
 
0
 

Definitely, it is the comparator that is not correct. It should be (strcmp() == 0) (strcmp returns 0 if the strings are equal). If you used a std::string, it would be even simpler.

Member Avatar
thekashyap
Practically a Posting Shark
809 posts since Feb 2007
Reputation Points: 193 [?]
Q&As Helped to Solve: 77 [?]
Skill Endorsements: 0 [?]
 
0
 

Perhaps you also need to create allocator for the key (char*).
It's been very long, but I do recall problems with char* as the key. You'll need to supply other support functions (like the comparator) to make it work.
E.g. when you insert into the map it just copies the address of key (char*) and stores inside the map, as the char* you passed was a transient it gets filled with junk once the function returns. So you need to create an operator = for char* which acts like strcpy().

As Mike said, use string instead, it works like charm:

#include<stdio.h>
#include<string.h>
#include<map>

using namespace std;
using namespace __gnu_cxx;

class row {
public:
    int number;
    char type;
    row& operator =(const row& r) {
        printf("\ninside operator=(): this = %x, r = %x", this, &r);
        this->number = r.number;
        this->type = r.type;
        return *this;
    }
    row() {
        printf("\ninside row(): this = %x", this, number, type);
        number = -1;
        type = '?';
    }
    row(const row& r) {
        number = r.number;
        type = r.type;
        printf("\ninside row(const row& %x): this = %x, %i, -%c-", &r, this, r.number, r.type);
    }
};

int main() {

    typedef map<string, row> t_mymap;
    t_mymap columns;

    row r1,r2;
    r1.number=1;
    r1.type='a';

    r2.number=2;
    r2.type='b';
    printf("\n&r1 = %x, &r2 = %x", &r1, &r2);

    printf("\nbefore first insert");
    columns.insert(t_mymap::value_type("first",r1));

    printf("\nbefore second insert");
    columns.insert(t_mymap::value_type("second",r2));

    printf("\nbefore printf");
    printf("\n%i %c\n",columns["first"].number,columns["first"].type);
    printf("\nColumns map size %i",columns.size());
    map<string, row>::iterator it = columns.begin();
    for ( ; it != columns.end(); it++) {
        printf("\n%s %i %c",it->first.c_str(),it->second.number,it->second.type);
    }
    printf("\nafter loop");

    return 0;
}
/*
Output:
inside row(): this = 22ff30
inside row(): this = 22ff20
&r1 = 22ff30, &r2 = 22ff20
before first insert
inside row(const row& 22ff30): this = 22ff14, 1, -a-
inside row(const row& 22ff14): this = 3e2be4, 1, -a-
before second insert
inside row(const row& 22ff20): this = 22fef4, 2, -b-
inside row(const row& 22fef4): this = 3e2c2c, 2, -b-
before printf
1 a

Columns map size 2
first 1 a
second 2 b
after loop
*/

Also I noticed that with char* there are very huge number of calls to c'tor, copy-c'tor and operator =. Can't explain all, but if you're bent upon using char* instead of string as key, may be it helps you understand what's happening and supply appropriate additional functions to map.

#include<stdio.h>
#include<string.h>
#include<map>

using namespace std;
using namespace __gnu_cxx;

struct row {
    int number;
    char type;
    row& operator =(const row& r) {
        printf("\ninside operator=(): this = %x, r = %x", this, &r);
        this->number = r.number;
        this->type = r.type;
        return *this;
    }
    row() {
        printf("\ninside row(): this = %x", this);
        number = -1;
        type = '?';
    }
    row(const row& r) {
        number = r.number;
        type = r.type;
        printf("\ninside row(const row& %x): this = %x, %i, -%c-", &r, this, r.number, r.type);
    }
};

struct eqstr {

    bool operator()(const char* s1, const char* s2) const {
        printf("\ninside operator(): -%s-, -%s-", s1, s2);
        return (strcmp(s1,s2) == 0);
    }
};


int main() {

    map<const char*, struct row, eqstr> columns;
    struct row r1,r2;
    printf("\n&r1 = %x, &r2 = %x", &r1, &r2);
    r1.number=1;
    r2.number=2;
    r1.type='a';
    r2.type='b';
    printf("\nbefore first insert");
    columns["first"]=r1;
    printf("\nbefore second insert");
    columns["second"]=r2;
    printf("\nbefore printf");
    printf("\n%i %c\n",columns["first"].number,columns["first"].type);
    printf("\nColumns map size %i",columns.size());
    map<const char*, struct row, eqstr>::iterator it = columns.begin();
    for ( ; it != columns.end(); it++) {
        printf("\n%s %i %c",it->first,it->second.number,it->second.type);
    }
    printf("\nafter loop");

    return 0;
}

/*
output:
inside row(): this = 22ff30
inside row(): this = 22ff20
&r1 = 22ff30, &r2 = 22ff20
before first insert
inside row(): this = 22fe40
inside row(const row& 22fe40): this = 22fe54, -1, -?-
inside row(const row& 22fe54): this = 3e2bc4, -1, -?-
inside operator=(): this = 3e2bc4, r = 22ff30
before second insert
inside operator(): -first-, -second-
inside operator(): -second-, -first-
inside operator=(): this = 3e2bc4, r = 22ff20
before printf
inside operator(): -first-, -first-
inside row(): this = 22fe40
inside row(const row& 22fe40): this = 22fe54, -1, -?-
inside operator(): -first-, -first-
inside row(const row& 22fe54): this = 3e2bec, -1, -?-
inside operator(): -first-, -first-
inside operator(): -first-, -first-
inside row(): this = 22fe40
inside row(const row& 22fe40): this = 22fe54, -1, -?-
inside operator(): -first-, -first-
inside row(const row& 22fe54): this = 3e2c14, -1, -?-
inside operator(): -first-, -first-
-1 ?

Columns map size 3
first -1 ?
first 2 b
after loop
*/
You
This article has been dead for over three months: Start a new discussion instead
Post:
Start New Discussion
View similar articles that have also been tagged: