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.

Recommended Answers

All 3 Replies

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));

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.

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
*/
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.