Using string* as a key in an unordered_set - c++

I would like to put use a string* as a key in an unordered_list. I do not want the hash the pointer itself but the string it points to.
I understand I need to create a struct like this:
struct myhash{
size_t operator()(const string * str){
return hash(*str);
}
}
and send it as a a hasher to the map template, but i am not sure how.

That's basically it. You then provide it as the third template parameter to the unordered_map type (Which I will assume to be the C++0x one). I would generalize it so it's usable in any situation, rather than just string:
struct dereference_hash
{
template <typename T>
std::size_t operator()(const T* pX)
{
return std::hash<T>()(*pX);
}
};
typedef std::unordered_map<std::string*, int, dereference_hash> map_type;

Related

Multidimensional map template accessed and assigned inside of class method

I was wondering if this was a valid way to assign a multidimensional map inside of a class method and if not how would I go about doing this.
template<typename T>
std::map<std::string, std::map<std::string, T>> MT;
template<typename T>
void MonsterTemplate(std::string name, std::string node, template T v) {
MT[name][node] = v;
}
Edit1: I compiled and it gave me many errors but I will just give a portion of the 1st.
error C3376: 'MonsterType::MT': only static data member templates are allowed
Edit2:
I tried creating a struct
template<typename T>
struct Wrapper
{
typedef std::map<std::string, std::map<std::string, T>> MT;
};
I then added this inside the class
template<typename T>
Wrapper<T>::MT mt;
template<typename T>
void MonsterTemplate(std::string name, std::string node, template T v) {
mt[name][node] = v;
}
Then got this error amongst many others.
warning C4346: 'MT': dependent name is not a type
The struct works outside the class without being multidimensional map, but I am unsure how to access it as multidimensional map.. just trying different things.
The idea is I want to store data of several objects data and index them by name, node and value.
Edit3:
So this is what I went with, and haven't gotten an error (just yet :p)
std::map<std::string, std::map<std::string, int>> MT;
void MonsterTemplate(std::string name, std::string node, int v) {
MT[name][node] = v;
}
Just updating this for anyone looking for something similar
So I figured out how to construct this.
template<typename T>
struct Test
{
std::map<std::string, std::map<std::string, std::map<size_t, std::map<std::string, T>>>> testmap;
void MonsterTemplate(std::string creatureName, std::string name, std::string node, T v) {
size_t i = testmap[creatureName][name].size();
testmap[creatureName][name][i][node] = v;
}
};
Test<std::string> str;
Since I needed various data types for T the structure worked much better than trying to find a way to assign a template to a static class, I am only using std::string as an example.
This can then be used inside of the class's method since str has a global scope.
str.MonsterTemplate(creatureName, name, node, value);

Custom hash that works with pointer and reference

I want to create a reusable IdHash and IdEqualTo class that takes const instances (reference, raw pointer, or smart pointer), and returns the hash value or the compare result.
template<class Entity, class Id>
struct IdFunc {
typedef typename std::function<const Id& (const Entity&)> type;
};
template<class Entity, class Id>
struct IdHash {
public:
explicit IdHash(const typename IdFunc<Entity, Id>::type& idFunc) : idFunc_(idFunc) {}
std::size_t operator()(const Entity& o) const {
return std::hash<Id>()(idFunc_(o));
}
private:
typename IdFunc<Entity, Id>::type idFunc_;
};
// IdEqualTo follows the same pattern
My first attempt works with unordered_set<MyClass, IdHash<string, MyClass>, IdEqualTo<...>>. Since MyClass will be a inheritance hierarchy instead of a single type, I need to switch to pointers: unordered_set<unique_ptr<MyClass>, IdHash<string, MyClass>, ...>. Now I need a version of the operator that takes unique_ptr&. I provided the following
std::size_t operator()(const Entity* o) const {
return std::hash<Id>()(idFunc_(*o));
}
hoping unique_ptr<MyClass>& can somehow be converted to MyClass*. It didn't work. Since this utility is supposed to transcend storage type, how can I make it work with reference, raw pointer, or smart pointer?
See code sample.
Thanks.
There is no automatic conversion from smart pointers to raw pointers (though you can use get()).
Specialize your templates for smart pointers, this is the way it is done in standard library and in boost.
template <class Inner, class Id>
struct IdHash<std::unique_ptr<Inner>, Id> {
typedef std::unique_ptr<Inner> PtrType;
std::size_t operator() (const PtrType &pointer) const {
return std::hash<Id>()(idFunc_(pointer.get());
}
};
You may also want to create std::hash<Id> instance only once instead of every time you call operator().

Access the element of an unordered_set with a hash

template<class T>
struct handle{
...
std::size_t index;
...
};
template<class T>
struct foo{
...
struct eq {
bool operator()(const std::shared_ptr<handle<T>> &a,
const std::shared_ptr<handle<T>> &b) const
{
return a->index == b->index;
}
};
struct hash {
std::size_t operator()(const std::shared_ptr<handle<T>> &a) const
{
return a->index;
}
};
std::unordered_set <std::shared_ptr<handle<T>>, hash, eq> handle_set;
...
};
The handle_set is a view into some std::vector<T>. I basically want to check if someone has a reference to an element in that vector like this
std::size_t index_from_vector = 5;
if(handle_set.count(index_from_vector)){
//handle exisits
}
But this doesn't work because an unordered_set needs the key type, so I would have to do it like this
auto sp = std::make_shared<handle<T>>(..,index_from_vector,..);
if(handle_set.count(sp)){
//handle exisits
}
That means I always would have to create a dummy shared_ptr if I want to check if there is a handle to a specific element in a vector.
Is there a way to access an unordered_set with only the hash?
I currently use a unordered_map for this
std::unordered_map<std::size_t, std::shared_ptr<handle<T>>> handle_map;
But updating handle_map is a bit of a pain because I would need to update handle.index and the key. This gets a bit awkward and an unordered_set would greatly simplify this.
Maybe there is even another data structure that would fit better?

unordered map without hashing

I'd like to use a structure just like std::map but without ordering, I don't need ordering and my key is pretty huge, so it's "less than" comparision takes time.
So, I saw unordered_map but it has a hash template argument, so, how to use unordered_map without hashing? I'll really need to build my own container?
This question applies to std::set too.
EDIT
Some answers have suggested to create my own hash, but I can't do this, I should have specified it here. The key contains floating point data, so hashing it would be a real bad idea. I need to compare (std::equal_to) directly.
Create your own hash, it's easily done by composing the overloads of std::hash on the fields of your key.
The cppreference example (same as previous link) is quite good (even if you do not need the template stuff):
struct S
{
std::string first_name;
std::string last_name;
};
template <class T>
class MyHash;
template<>
class MyHash<S>
{
public:
std::size_t operator()(S const& s) const
{
std::size_t h1 = std::hash<std::string>()(s.first_name);
std::size_t h2 = std::hash<std::string>()(s.last_name);
return h1 ^ (h2 << 1);
}
};
After that you can use it in the std::unorderd_map:
std::unordered_map<S, Value, MyHash<S>> the_map;
By the way std::unordered_set also need a hash.
You need to spetialize hash object for your key before declaring your unordered_map.
namespace std
{
template <>
class hash<Key>
{
public:
size_t operator()(const Key &) const
{
// ... your hash function for Key object ...
}
};
}
std::unordered_map<Key, Value> myMap;
Example, if I want you use as a key pair:
namespace std
{
class hash<pair<string, int>>
{
public:
size_t operator()(const pair<string, int> &s) const
{
size_t h1 = hash<string>()(s.first);
size_t h2 = hash<int>()(s.second);
return h1 ^ (h2 << 1);
}
};
}
unordered_map<pair<string, int>, string> myMap;

Switch template type

I want to make some storage for my game. Now the code looks like:
class WorldSettings
{
private:
std::map<std::string, int> mIntegerStorage;
std::map<std::string, float> mFloatStorage;
std::map<std::string, std::string> mStringStorage;
public:
template <typename T>
T Get(const std::string &key) const
{
// [?]
}
};
So, I have a few associative containers which stores the exact type of data. Now I want to add into settings some value: settings.Push<int>("WorldSize", 1000); and get it: settings.Get<int>("WorldSize");. But how to switch need map due to passed type into template?
Or, maybe, you know a better way, thanks.
If your compiler supports this1, you can use template function specialisations:
class WorldSettings
{
private:
std::map<std::string, int> mIntegerStorage;
std::map<std::string, float> mFloatStorage;
std::map<std::string, std::string> mStringStorage;
public:
template <typename T>
T Get(const std::string &key); // purposely left undefined
};
...
template<>
int WorldSettings::Get<int>(const std::string& key) {
return mIntegerStorage[key];
}
template<>
float WorldSettings::Get<float>(const std::string& key) {
return mFloatStorage[key];
}
// etc
Notice that the methods are not const because map<>::operator[] is not const.
Also, if someone tries to use the template with a type other than one you have provided a specialisation for, they will get linker errors, so your code won't misbehave or anything. Which is optimal.
1 If not, see #gwiazdorrr's answer
First of all, since prior to C++11 you can't specialise functions, your member functions must differ in signature - return type does not count. From my experience on some compilers you can do without it, but as usual - you should keep your code as close to standard as possible.
That said you can add a dummy paramater that won't affect performance and the way you call function:
public:
template <typename T>
T Get(const std::string &key) const
{
return GetInner(key, (T*)0);
}
private:
int GetInner(const std::string& key, int*) const
{
// return something from mIntegerStorage
}
float GetInner(const std::string& key, float*) const
{
// return something from mFloatStorage
}
And so on. You get the idea.
Seth's answer is ideal, but if you don't have access to a C++11 compiler, then you can use template class specialization and do this instead. It's much more verbose, but keeps the same functionality.
class WorldSettings
{
template<class T>
struct Selector;
template<class T>
friend struct Selector;
private:
std::map<std::string, int> mIntegerStorage;
std::map<std::string, float> mFloatStorage;
std::map<std::string, std::string> mStringStorage;
public:
template <typename T>
T Get(const std::string &key)
{
return Selector<T>::Get(*this)[key];
}
};
template<>
struct WorldSettings::Selector<int>
{
static std::map<std::string, int> & Get(WorldSettings &settings)
{
return settings.mIntegerStorage;
}
};
template<>
struct WorldSettings::Selector<float>
{
static std::map<std::string, float> & Get(WorldSettings &settings)
{
return settings.mFloatStorage;
}
};
// etc.
In C++03 I would recommend the use of ‘boost::any‘ in the type of the container, and the. You need a single accessor:
std::map<std::string,boost::any> storage;
template <typename T> getValue( std::string const & key ) {
return boost::any_cast<T>( storage[key] );
}
This is a rough sketch, as a member function It would be const, and it should use ‘map::find‘ not to modify the container when searching, it should deal with invalid joeys, and probably remap the boost exceptions into your own application exceptions.