How to use std::function when implementing a HashMap in C++ - c++

I am working on HashMap class with typedef std::function in HashMap class declaration.
typedef std::function<unsigned int(const std::string&)> HashFunction;
For private member of the class, I have HashFunction hash that can be used with my own hash function or use other function supplied to constructor.
HashFunction hash;
unsigned int myHashFunction(const std::string&) const;
A default constructor must set the hash to default value which in my case is myHashFunction. And a constructor which a HashFunction as parameter must use that particular function, not myHashFunction.
HashMap::HashMap()
: map(new Node*[INITIAL_BUCKET_COUNT]), mapSize(0), mapCapacity(INITIAL_BUCKET_COUNT),
hash(std::bind(&HashMap::myHashFunction, this)) // This is definitely not correct
{
initializeMap();
}
HashMap::HashMap(HashFunction hashFunction)
: map(new Node*[INITIAL_BUCKET_COUNT]), mapSize(0), mapCapacity(INITIAL_BUCKET_COUNT),
hash(hashFunction) //Is this correct?
{
initializeMap();
}
How can I bind myHashFunction or a supplied hash function to hash, so that I can use hash(key) in the class member functions to support both hash functions? Please direct me to the right path if I am completely getting it wrong. Thanks.

For the std::bind line, if you're trying to bind a member function then you need to include a placeholder, like so:
std::bind(&HashMap::myHashFunction, this, std::placeholders::_1);
You might find it better to make myHashFunction a static member function though, unless it actually uses other members or data in your HashMap (my guess is it probably shouldn't).

Why don't you use std::hash? It's defined in the <functional> header.

Related

Using hash directly in methods from unordered_map instead of the user defined object producing the hash

So i have created a class that i use as a hash in an unordered_map
class MyClass
{
hash_type mHash = 0;
hash_type hash() { return mHash; }
bool operator!= (const MyClass& rhs) const;
bool operator== (const MyClass& rhs) const;
}
namespace std
{
template <>
struct hash<MyClass>
{
hash_type operator()(const MyClass& k) const noexcept
{
return k.hash();
}
};
}
It works as expected but i would like to add some functionality.
I would like to be able to use the hash itself when using unordered_map functions like find and erase.
Now i have to do this:
void _erase_key(const MyClass& key) { umap.erase(key); }
But i would like to be able to do this as well:
void _erase_key(const hash_type key) { umap.erase(key); }
Is it possible to somehow use the hash directly instead of the object producing the hash when using methods like find and erase?
If I understand you right, you want to have an std::unordered_map<MyClass, Value> such that you can also query with hash_type and you have hash_type h == MyClass m if std::hash<MyClass>{}(m) == h. Is this correct?
This is not possible in C++17. With C++20, there will be added the functionality of transparent hashes. You can read about that very briefly here. With this, your map has to fulfill certain properties
Your equality type Eq must provide a member type Eq::is_transparent, i.e. you have to put a using is_transparent = some_type; in it. (The exact type is without consequence.)
An object of type Eq has to provide an overload to compare all possible combinations of types you want to use. I.e. provide overloads for (MyClass, MyClass) and (MyClass, hash_type).
Your hash type Hash has to provide a member type Hash::transparent_key_equal, so again, put using transparent_key_equal = some_type; in it.
An object of type Hash must be callable with every type you want to use. I.e. you have to have an operator() overload for both MyClass and hash_type.
For Eq you can use std::equal_to<> (note the empty diamond!) if you provide publically accessible operator== for the appropriate types. This is NOT the default for unordered_map.
There is to the best of my knowledge no analagon for std::hash, so you have to add an own type for that and provide it to the map.
Pre-C++20 the only thing you can do if you want to keep your key type is to write a conversion from hash_type to MyClass.
But I sense a fundamental problem in this, namely that you see two objects of MyClass as identical if they have the same hash. If this is unintentional, you should really fix this. If it is intentional, make sure, that operator==(MyClass, MyClass) also only compares hashes.
In the later case, there is an easy fix for your problem. Change your map to std::unordered_map<hash_type, Value> and hash each MyClass you use to query the map. If you need a reverse lookup to get MyClass from a hash, either write a conversion function from hash_type to MyClass if this is possible, otherwise add another std::unordered_map<hash_type, MyClass>, where you store every object of type MyClass you ever use.

Turning a private struct hasher operator into static

I have implemented a class A which holds an unordered_set of class B instances, using a custom hasher function.
File a.hh:
#include "b.hh"
class A {
private:
struct HashB_ {
size_t operator()( const B & ) const ;
};
typedef std::unordered_set< B, A::HashB_ > HashTable_t_ ;
HashTable_t_ hash_table_;
};
File a.cpp:
#include "a.hh"
size_t
A::HashB_::operator()( const B & b ) const {
return b.getHash();
}
Where b.getHash() implements the actual hashing.
My goal is to make this private hasher function static, if possible, as it does not interact at all with the members of class A. Defining the hasher outside of class A would be an alternative, but I wish to hide the implementation outside of class A.
If I try to declare static size_t operator()( const B & ); I get the following compilation error: must be a nonstatic member function.
And if I declare static struct HashB_ I get the following error: a storage class can only be specified for objects and functions.
Is it possible do turn this private hasher into a private "static" hasher? And would it make any sense in doing so? Or should I do it with a friend hasher, instead, to hide the implementation?
The function call operator does require an object to work on even if they don't need to touch the object. That's a deliberate choice to establish the relevant context for looking up the function call operator. That operator can't be static.
You could use a static function in your class A. However, the unordered containers use an object to specify the hashing operation. A function pointer to the static member is a suitable object. However, you'd need to use a function pointer type for the hasher template argument and pass the function pointer to the constructor of the unordered cintainer. Aside to this syntactic complication this approach probably has a negative performance impact: conpilers are really good at inlining member functions which do not touch any if the member. They do have a hard time doing the appropriate analysis with function pointers, even in simple looking cases.
tl;dr: don't worry about the hash object not having any member and the function call operator being non-static: it is likely the most effective approach!

Using functor with definable state as unordered_set hash function

I'm trying to use a functor with definable state as the hasher for an unordered_set, the problem i'm faced with is I doesn't know how to initialize the functor passed as the template parameter. It would be something like this.
class A{
private:
class Hasher{
private:
int a;
public:
Hasher(int val=3):a(val){};
size_t operator()(const string & s) const{
return s[0]*a;
}
};
unordered_set<string,Hasher??> us;
int hasher_val;
public:
A(int h_val):hasher_val(h_val){};
}
The problem is, how can I define "a" for a value different to 3?
std::unordered_set's constructor has optional parameters which can be used to initialize its hash instance:
unordered_set<string,Hasher> us;
int hasher_val;
public:
A(int h_val) : us{51, Hasher(4)}, hasher_val{h_val}{};
One slightly uncomfortable fact is that the hash instance is the second parameter, and you have to explicitly specify your hash bucket size, instead of relying on the wisdom of your C++ library to provide a suitable default (in my example, I just picked 51 off the top of my head, which is probably very, very wrong but that would be a different question to mull over...).
You should spend a few minutes digging into your header files to determine what default your C++ implementation uses, for the set's bucket size, and supply the same value.
P.S. The same approach is used with all library containers: their constructors' parameters are all defaulted, making it possible to explicitly construct them using a custom hash class instance, a custom comparator class instance, etc...

Returning an object which is defined by another local object

I am wondering how to make a function return an object that is dependent of another object or pointer which does not exist outside the function. The situation is the following:
To sort a std::map there is a custom comparator function declared inside a class Object. This function must be a static member of a class or a global function in order to be passed to std::map:
static bool RenderOrderComparator(const Object *Obj, float a, float b);
This function must access some member of an instance of Object to compare two given floating point numbers correctly (non-trivial). Now I want to have another function which is used at several places in the respective unit to do the sorting:
X Object::SortInstances(const Camera &Cam)
{
auto comp = std::bind(RenderOrderComparator, this, std::placeholders::_1, std::placeholders::_2);
std::map<float, const ObjectTransformation &, decltype(comp)> SortedInstances(comp);
// ... do some stuff and fill the map
return SortedInstances;
}
The idea here was to use std::bind to bind this to the comparator function in order to access the required member. My question is now what do I have to write in the place of X? What is the type of the map I am actually intending to return? I tried something like
std::map<float, const ObjectTransformation &, decltype(std::bind(Object::RenderOrderComparator, this, std::placeholders::_1, std::placeholders::_2))>
which logically cannot work because this is not known outside the definition of the function. Also I played - unsuccessfully - with std::result_of. Do you have any solution for that problem?
I am looking foreward to your help.
Yours Julian
This function must be a static member of a class or a global function in order to be passed to std::map
Where did this requirement come from? std::map supports comparators that are not functions, but objects with operator() a.k.a. functors.
struct RenderOrderComparator {
Object const * scene_or_whatever;
bool operator() ( float a, float b ) {
return … ;
}
};
Use RenderOrderComparator as the third template argument to std::map. There should be no need for std::bind, which as it happens only produces such an object with an operator() member.

How to use a non-static member function in Hasher?

I need to establish a hash table using a hasher different from the default one, so I write something like:
class foo {
public:
...
private:
struct myhasher {
size_t operator() (myclass bar) { return hash_calculation bar; }
}
static size_t hash_calculation (myclass bar) {
// do some calculation
}
hash_map<myclass, myhasher> myhashmap;
}
It works. Now for some reason I have to write a non-static member function to replace hash_calculation, say, it needs a non-static member of the class as an argument. Then the whole thing failed because I cannot use a non-static method in a nested struct.
This is somehow similar to another widely discussed problem: how to use a non-static function to do comparison or sorting. See for example:
Using a non-static class member inside a comparison function
and
C++ std list sort with custom comparator that depends on an member variable for the object instance . They both established a functor instead of a function as the comparator. However in my case this trick does not work because I need a class name inside the hash_map definition, not a specific struct object. What should I do? Thanks in advance for your help!
You can't. How is the hash_map supposed to know which instance of myhasher should be used when calling myhaser::hash_calculation?
hash_map isn't part of the standard C++ library, not even in C++11, so it's a custom class, and you have included no information about how it works. If there is a way for it to take some sort of constructor argument for which myhasher it should use, you're in luck. But it doesn't sound like it.
Also, you're using pass by value when you probably mean to pass in a const reference. Passing by value is likely going to be really slow and inefficient.
The standard "hash-map", i.e., std::unordered_map<K, V, H, E, A> takes a hash object of type H as constructor argument. A copy of this object is used to determine the hash for the object by way of the function call operator. This way can provide some context. Obviously, you were already using a non-static function call operator but you choose to delegate to a static member.