简体   繁体   中英

std::unordered_map find() operation not working in GCC7

I am working on porting my c++ application from GCC4.7 to GCC7 and ran into an issue where std::hash_map find() function returns null result for keys which are present in map.

Existing code:

struct eqfunc {
  bool operator()(const char* const &s1, const char* const &s2) const {
    std::cout << "eqfunc in action " << s1 << " - " << s2 << std::endl;
    return strcmp(s1,s2) == 0;
  }
};

template <typename T> class customMap : public std::hash_map<const char*,T,std::hash<const char*>,eqfunc> {};

customMap<const char*> cstmMap;
std::cout << "Insert abc" << std::endl;
cstmMap["abc"] = "ABC";
std::cout << "Insert def" << std::endl;
cstmMap["def"] = "DEF";
std::cout << "Insert xyz" << std::endl;
cstmMap["xyz"] = "XYZ";

std::cout << "Find def in cstmMap" << std::endl;
string findString("def");
customMap<const char*>::iterator ptr = cstmMap.find((char *)findString.c_str());
LOG_INFO("output ptr %s", ptr);

This was working fine in GCC4.7 platform. When I ported the code to GCC7, I noticed the behaviour where find() returns null result even for keys which are present in the map.

Sample run output in GCC7

Insert abc
Insert def
Insert xyz
Find def in cstmMap
output ptr (null)

Updated std::hash_map to std::unordered_map doesn't work either:

template <typename T> class customMap : public std::unordered_map<const char*,T,std::hash<const char*>,eqfunc> {};

Another weird behaviour I noticed using std::unordered_map is that eqfunc is not executed in consistent pattern across multiple runs

Sample 1 run

Insert abc
Insert def
eqfunc in action def - abc
Insert xyz
Find def in cstmMap
eqfunc in action def - xyz
output ptr (null)

Sample 2 run

Insert abc
Insert def
eqfunc in action def - abc
Insert xyz
Find def in cstmMap
output ptr (null)

NOTE: This is very large code base and changing const char * to std::string is not straightforward and requires a lot of work.

I am wondering if there are any work arounds to get it working with existing const char * data type for the map's key. Any help on this would be appreciated.

You've discovered that std::hash<const char*> hashes the actual pointer - not the C string it points at. Sometimes "def" and the second "def" will actually have the same pointer value. It depends on how the compiler optimized it.

To use C strings, you need to provide a hash functor for C strings. Here's one example:

#include <string_view>

struct cstring_hash {
    size_t operator()(std::string_view str) const {
        return std::hash<std::string_view>{}(str);
    }
};

And redefine the container:

template <typename T>
class customMap : public std::unordered_map<const char*, T, cstring_hash, eqfunc> {
    // To be able to use ctors:
    using std::unordered_map<const char*, T, cstring_hash, eqfunc>::unordered_map;
};

The added using of the unordered_map s constructors makes it possible to construct the map in a simpler way:

int main() {
    customMap<const char*> cstmMap{
        {"abc", "ABC"},
        {"def", "DEF"},
        {"xyz", "XYZ"},
    };

    std::string findString("def");
    auto ptr = cstmMap.find(findString.c_str());
    std::cout << ptr->second << '\n';            // prints DEF
}

If you are using a C++ version prior to C++17, you could replace cstring_hash by picking a good enough hash function. Here's one that probably does the job:

namespace detail {
    static const auto S = // shift constant
        sizeof(size_t) < sizeof(uint64_t) ? 16u : 32u;
    static const auto C = // multiplication constant
        sizeof(size_t) < sizeof(uint64_t) ? 23456789u : 0xBB67AE8584CAA73Bull;
}

#if __cpp_constexpr >= 201304L
  #define RELAXEDCONSTEXPR constexpr
#else
  #define RELAXEDCONSTEXPR
#endif

struct cstring_hash {
    RELAXEDCONSTEXPR size_t operator()(const char *s) const {
        size_t h = 0;
        
        for(; *s; ++s) {
            h = h * detail::C + static_cast<unsigned char>(*s);
            h ^= h >> detail::S;
        }
        
        return h *= detail::C;
    }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM