Skip to content

Instantly share code, notes, and snippets.

@facontidavide
Last active June 6, 2023 22:22
Show Gist options
  • Save facontidavide/95f20c28df8ec91729f9d8ab01e7d2df to your computer and use it in GitHub Desktop.
Save facontidavide/95f20c28df8ec91729f9d8ab01e7d2df to your computer and use it in GitHub Desktop.
Use std::string_view to find() an element in std::unordered_map<string,T>, avoiding potential memory allocations
#include <iostream>
#include <cstring>
#include <unordered_map>
// https://github.com/martinmoene/string-view-lite
#include "string_view.hpp"
template <typename Value>
class StringMap: public std::unordered_map<std::string, Value>
{
public:
typename std::unordered_map<string,Value>::iterator find(const nonstd::string_view& v )
{
tmp_.reserve( v.size() );
tmp_.assign( v.data(), v.size() );
return std::unordered_map<string, Value>::find(tmp_);
}
typename std::unordered_map<std::string,Value>::iterator find(const std::string& v )
{
return std::unordered_map<std::string, Value>::find(v);
}
typename std::unordered_map<std::string,Value>::iterator find(const char* v )
{
tmp_.assign(v);
return std::unordered_map<std::string, Value>::find(v);
}
private:
thread_local static std::string tmp_;
};
template <typename T> thread_local std::string StringMap<T>::tmp_ = {};
int main()
{
std::string key_A("Hello");
nonstd::string_view key_B("Hello");
nonstd::string_view key_C(key_A);
StringMap<std::string> smap;
smap["Hello"] = "World";
for (auto& it: smap)
{
std::cout << it.first << ":" << it.second << std::endl;
}
std::cout << smap.find(key_A)->second << std::endl;
std::cout << smap.find(key_B)->second << std::endl;
std::cout << smap.find(key_C)->second << std::endl;
std::cout << smap.find("Hello")->second << std::endl;
return 0;
}
@Oipo
Copy link

Oipo commented Apr 21, 2020

I had a use case where I wanted to define a typedef for more than just a string map. To do so, add something like the following:

    template <typename Key, typename T>
    struct map_type {
        typedef std::unordered_map<Key, T> type;
    };

    template <typename T>
    struct map_type<string, T> {
        typedef StringMap<T> type;
    };

    template <typename Key, typename T>
    using flat_map = typename map_type<Key, T>::type;

@kolomenkin
Copy link

As for me the solution does not look thread safe.

Find function may be interrupted in the middle by OS and another thread may be scheduled in the same thread executing the same code and reusing the same std::string buffer.

@facontidavide
Copy link
Author

unordered_map itself is not thread safe ;)

@kolomenkin
Copy link

kolomenkin commented Apr 30, 2022

std::unordered_map::find is originally thread safe against itself and other readonly methods. But your solution is not.

If I implement shared_mutex pattern with multiple simultaneous readers your implementation may corrupt memory.

I'm writing here to notify others since this code is easily found via google, I saw people citing this solution in stackOverflow. But this solution is broken and may corrupt memory in multi threaded environment.

@Vizepi
Copy link

Vizepi commented Jun 4, 2023

@kolomenkin Are you confusing hardware threads and execution threads?
If the OS schedules another execution thread on the hardware thread the previous code was running on, the thread_local variable will not be the same.

@kolomenkin
Copy link

kolomenkin commented Jun 6, 2023

@Vizepi , you are right. It was a silly mistake on my side.

@facontidavide , your solution does not have the problems I mentioned above. It is thread safe for reading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment