Using char* as a key in std::map
I am trying to figure out why the following code is not working, and I am assuming it is an issue with using char* as the key type, however I am not sure how I can resolve it or why it is occuring. All of the other functions I use (in the HL2 SDK) use char*
so using std::string
is going to cause a lot of unnecessary complications.
std::map<char*, int> g_PlayerNames;
int PlayerManager::CreateFakePlayer()
{
FakePlayer *player = new FakePlayer();
int index = g_FakePlayers.AddToTail(player);
bool foundName = false;
// Iterate through Player Names and find an Unused one
for(std::map<char*,int>::iterator it = g_PlayerNames.begin(); it != g_PlayerNames.end(); ++it)
{
if(it->second == NAME_AVAILABLE)
{
// We found an Available Name. Mark as Unavailable and move it to the end of the list
foundName = true;
g_FakePlayers.Element(index)->name = it->first;
g_PlayerNames.insert(std::pair<char*, int>(it->first, NAME_UNAVAILABLE));
g_PlayerNames.erase(it); // Remove name since we added it to the end of the list
break;
}
}
// If we can't find a usable name, just user 'player'
if(!foundName)
{
g_FakePlayers.Element(index)->name = "player";
}
g_FakePlayers.Element(index)->connectTime = time(NULL);
g_FakePlayers.Element(index)->score = 0;
return index;
}
Solution 1:
You need to give a comparison functor to the map otherwise it's comparing the pointer, not the null-terminated string it points to. In general, this is the case anytime you want your map key to be a pointer.
For example:
struct cmp_str
{
bool operator()(char const *a, char const *b) const
{
return std::strcmp(a, b) < 0;
}
};
map<char *, int, cmp_str> BlahBlah;
Solution 2:
You can't use char*
unless you are absolutely 100% sure you are going to access the map with the exact same pointers, not strings.
Example:
char *s1; // pointing to a string "hello" stored memory location #12
char *s2; // pointing to a string "hello" stored memory location #20
If you access map with s1
you will get a different location than accessing it with s2
.
Solution 3:
Two C-style strings can have equal contents but be at different addresses. And that map
compares the pointers, not the contents.
The cost of converting to std::map<std::string, int>
may not be as much as you think.
But if you really do need to use const char*
as map keys, try:
#include <functional>
#include <cstring>
struct StrCompare : public std::binary_function<const char*, const char*, bool> {
public:
bool operator() (const char* str1, const char* str2) const
{ return std::strcmp(str1, str2) < 0; }
};
typedef std::map<const char*, int, StrCompare> NameMap;
NameMap g_PlayerNames;
Solution 4:
You can get it working with std::map<const char*, int>
, but must not use non-const
pointers (note the added const
for the key), because you must not change those strings while the map refers to them as keys. (While a map protects its keys by making them const
, this would only constify the pointer, not the string it points to.)
But why don't you simply use std::map<std::string, int>
? It works out of the box without headaches.
Solution 5:
You are comparing using a char *
to using a string. They are not the same.
A char *
is a pointer to a char. Ultimately, it is an integer type whose value is interpreted as a valid address for a char
.
A string is a string.
The container works correctly, but as a container for pairs in which the key is a char *
and the value is an int
.