User-defined hash function for unordered_map - c++

I am trying to create a templated wrapper class on stl unordered_map. I am passing the hash function class as a template parameter to the wrapper class and provided a string specialization. The below code compiles and works but if the commented part is included then compilation error saying:
"/usr/include/c++/6/bits/unordered_map.h:143:28: error: no matching
function for call to ‘HashFunction
::HashFunction()’
const hasher& __hf = hasher(),".
However, I am bound to have the ctor of the hash function class. I tried various ways but could not make it work. Please provide your thoughts/comments.
#include <iostream>
#include <unordered_map>
using namespace std;
template< class Key >
class HashFunction
{
public:
//HashFunction( const Key & inKey );
size_t operator()(const Key &inKey) const;
private:
unsigned mHashCode;
};
//template<>
//HashFunction< string >::HashFunction( const string & inKey )
//{
//mHashCode=std::hash<string>{}(inKey);
//}
template <>
size_t HashFunction< string >::operator()(const string &inKey) const
{
return std::hash<string>{}(inKey);
//return mHashCode;
}
template< class Key, class Val, class Hash = HashFunction< Key > >
class unordered_map_wrapper
{
public:
unordered_map_wrapper();
private:
unordered_map<Key, Val, Hash> * mTable;
};
template< class Key, class Val, class Hash >
unordered_map_wrapper< Key, Val, Hash >::unordered_map_wrapper()
{
mTable=new unordered_map<Key, Val, Hash>(10);
}
int main() {
unordered_map_wrapper<string, unsigned> h;
return 0;
}

This is not how the Hash template parameter is intended to work in std::unordered_map! The map creates one single Hash instance (using the default constructor, so you need to provide it!) which is used for all hash calculations needed further. So your class must look like this instead:
template<class Key>
class HashFunction
{
public:
// HashFunction(); <- can just leave it out...
size_t operator()(const Key& inKey) const;
};
template<>
size_t HashFunction<std::string>::operator()(const std::string& inKey) const
{
// calculate hash value in operator!!!
return std::hash<std::string>()(inKey);
}
To avoid constructing the std::hash all the time, though, I'd rather specialise the whole template class instead:
template<class Key>
class HashFunction; // leave entirely unimplemented
// (if it has no general meaning, at least...)
template<>
class HashFunction<std::string>
{
public:
size_t operator()(const std::string& inKey) const
{
return mHash(inKey);
}
private:
std::hash<std::string> mHash;
};
It does not make much sense re-implementing something that is already there, though. May I assume that you used std::string here just as a place holder for your own class?
By the way: A simple typedef would do the job easier than your wrapper class:
template <typename Key, typename Value>
using my_unordered_map = std::unordered_map<Key, Value, HashFunction<Key>>;
Maybe you are interested in alternatives, too? Lets first assume you have two classes (instead of std::string):
class C1
{ };
class C2
{ };
Most elegant solution (in my eyes at least) is simply specialising std::hash for your classes (while in general, it is illegal to add something to std namespace, template specialisations are the exception...):
namespace std
{
template<>
class hash<C1>
{
public:
size_t operator()(C const& c) const
{
return 0; // your implementation here...
}
};
// same for C2
}
// no typedef necessary, just use std::unordered_map directly:
std::unordered_map<C1, unsigned> m1;
std::unordered_map<C2, unsigned> m2;
You can provide your own HashFunction class, providing overloads:
class HashFunction
{
public:
size_t operator()(C1 const& c) const
{
return 0;
}
size_t operator()(C2 const& c) const
{
return 0;
}
};
Again, a typedef makes your life easier:
template <typename Key, typename Value>
using my_unordered_map = std::unordered_map<Key, Value, HashFunction>;
std::unordered_map<C1, unsigned, HashFunction> m1;
my_unordered_map<C1, unsigned> m1_;
std::unordered_map<C2, unsigned, HashFunction> m2;
my_unordered_map<C2, unsigned> m2_;
Finally, if you absolutely need to initialise your HashFunction with some parameters to configure it correctly, you can do so, but you have to deliver a pre-configured instance to your hash map then!
template<typename T>
class HashFunction
{
public:
HashFunction(double value);
size_t operator()(T const& c) const;
private:
double data;
};
template<typename T>
HashFunction<T>::HashFunction(double value)
: data(value)
{ }
template<>
size_t HashFunction<C1>::operator()(C1 const& c) const
{
return static_cast<size_t>(data);
};
template<>
size_t HashFunction<C2>::operator()(C2 const& c) const
{
return static_cast<size_t>(data);
};
template <typename Key, typename Value>
using my_unordered_map = std::unordered_map<Key, Value, HashFunction<Key>>;
my_unordered_map<C1, unsigned> m1(12, HashFunction<C1>(10.12));
my_unordered_map<C2, unsigned> m2(10, HashFunction<C2>(12.10));
Well, now, at least, your wrapper class can come in handy again:
template <typename Key, typename Value, typename Hash = HashFunction<Key>>
class unordered_map_wrapper
{
public:
unordered_map_wrapper()
: mMap(16, Hash())
{ }
unordered_map_wrapper(double parameter)
: mMap(16, Hash(parameter))
{ }
private:
std::unordered_map<Key, Value, Hash> mMap;
};
unordered_map_wrapper<C1, unsigned> m1(10.12);
unordered_map_wrapper<C2, unsigned> m2(12.10);
// possible due to provided default ctor:
unordered_map_wrapper<std::string, unsigned, std::hash<std::string>> m3;

It expects to have a default constructor, which is removed when you define your custom version.
This compiles:
template< class Key >
class HashFunction
{
public:
HashFunction(){};
HashFunction( const Key & inKey );
size_t operator()(const Key &inKey) const;
private:
unsigned mHashCode;
};

Related

How to declare a templated function so that can be passed in a class constructor/function

I want to pass in a user defined function to a class which requires a user defined matching function. In ye olde days of C I would have used a function pointer with void* arguments. But there must be a better way...
Here is roughly the sort of thing I want to do. One limitation I have is that the platform I am on has no standard library. But the basic core language C++11 is available.
What I need to do:
#include <iostream>
using namespace std;
// TODO - replace this C construct with C++ equivalent
//typedef bool(*match_key)(const void* key1, const void* key2);
// somehow declare this as a typedef? need a way to declare a signature in C++
typedef template<class T>
bool (*match_key)(const T& key1, const T& key2);
// *** User defined matching function
bool mymatcher(const int i, const int j) {
return i == j;
}
template<class K>
class hashmap {
public:
hashmap<K>(const K& key, match_key matchfunc) : key_(key), cmp(matchfunc) { }
bool matched(const K& key) {
return cmp(key_, key);
}
private:
const K key_;
match_key cmp;
};
int main()
{
int i = 3;
int j = 4;
hashmap<int> hm(i, mymatcher);
cout << "i matches j? " << (hm.matched(j) ? "yes" : "no") << endl;
return 0;
}
#include <iostream>
using namespace std;
// TODO - replace this C construct with C++ equivalent
//typedef bool(*match_key)(const void* key1, const void* key2);
// somehow declare this as a typedef? need a way to declare a signature in C++
typedef template<class T>
using match_key = bool (*)(const T& key1, const T& key2);
// *** User defined matching function
bool mymatcher(const int i, const int j) {
return i == j;
}
template<class K>
class hashmap{
public:
hashmap(const K& key, match_key<K> matchfunc) : key_(key), cmp(matchfunc) { }
bool matched(const K& key) {
return cmp(key_, key);
}
private:
const K key_;
match_key<K> cmp;
};
int main()
{
int i = 3;
int j = 4;
hashmap<int> hm(i, mymatcher);
cout << "i matches j? " << (hm.matched(j) ? "yes" : "no") << endl;
return 0;
}
If the T of the match_key is supposed to be the same as the K of the hashmap, you can make the signature part of the hashmap:
template <typename T>
struct hashmap {
typedef bool (*match_key)(const T& key1, const T& key2);
....
}
...otherwise I would make the type of the comparator a second template parameter:
template <typename K, typename C>
struct hashmap {
C cmp;
hashmap(const K& key, C matchfunc) : key_(key), cmp(matchfunc) { }
...
}
this would give the user greater flexibility but also opens the door to long compiler errors.
The way passing a function as a pointer is very restrictive in C++. It won't be able to container any callable object in C++. To be specific, it will block the use of functor in C++.
Here functor, referred to any type T that satisfies:
which has overloaded calling operator;
Or
has user-defined conversion functions that enables it to be statically casted to a function pointer.
It either case, any object of such type is callable, and can be used as if they are names of functions.
An example to this is std::less, which is frequently used when using algorithms that need to compare 2 objects.
In order to be able to pass any callable object, you have to templaterize the type of the function:
template <class K, class Cmp = std::less<>>
class hashmap {
public:
hashmap<K>(const K& key, Cmp _cmp = {}): key_(key), cmp(_cmp) { }
bool matched(const K& key) {
return cmp(key_, key);
}
private:
const K key_;
Cmp cmp;
};

Function of special template function

I have a structure template e.g.:
template<typename KEY_T, typename VAL_T>
struct pair
{
KEY_T key; VAL_T val;
};
VAL_T val may be the different (string, list or smth else)
What I want is to overload the operator[] for the specified template structure pair<std::string, std::list>, and only for it.
How is it possible?
P.S. I'm writing an Ini-parser and I want to access for settings like settings[Section][Key], where settings[Section] returns the pair<std::string, std::list<Entry>> and settings[Section][Key] then return the string from std::list<Entry>
Class templates may be specialized, either partially:
template<typename T>
struct pair<std::string, std::list<T>>
{
std::string key;
std::list<T> val;
T& operator[](...) {
//implement
}
};
or fully:
template<>
struct pair<std::string, std::list<Entry>>
{
std::string key;
std::list<Entry> val;
Entry& operator[](...) {
//implement
}
};
As a side note, consider putting your classes in a designated namespace. Easier to manage if you ever need to work with std::pair too.
you can do this instead.
template <typename KEY, typename VAL>
struct pair
{
KEY _key;
VAL _val;
pair (const KEY& key, const VAL& val):
_key(key), _val(val)
{};
// ... here is the trick
friend ENTRY& get(pair<std::string, std::list<ENTRY>>& p, size_t idx)
};
ENTRY& get(pair<std::string, std::list<ENTRY>>& p, size_t idx)
{
return p._val [idx];
};
your question is not clear, you said:
Settings[Section] -> pair <string, list<entry>>;
Settings[Section][Key]-> list<entry>;?? the whole list object??
what you are looking for can be done like this:
std::map<std::string, std::map<std::string, std::string>> Settings;
std::string my_value = Settings[Section][Key];
Settings is a map of maps:
[Color] <-- this is the section
pen = blue <-- this is the pair KEY:VALUE
border = green
canvas = black

Adding methods with template specialization

I have a hash table container called Map with the following method:
Value Map<Key, Value>::valueFor(const Key& key);
Unfortunately, the most used case is for Key = std::string where we usually call the method with string literals such as:
const Value v = map.valueFor("my_key");
We loose a few cycles creating the std::string. Therefore, I would like to add an overload
Value Map<std::string, Value>::valueFor(const char* key);
when Key = std::string. I am sure that the compiler can even compute the hash at compile time with such a signature which would also help to speedup things.
Is there a way to do that in C++11 without template specializing the whole Map class and rewrite all the methods?
You can just add another overload valueFor(char const * key). Probably you then also want to disable this overload with SFINAE if the Key is not std::string.
#include <iostream>
#include <string>
#include <type_traits>
template < typename Key, typename Value >
struct Map
{
Value valueFor(Key const& key)
{
std::cout << "valueFor(const Key& key)\n";
return Value{};
}
template < typename _Key = Key,
typename = typename std::enable_if< std::is_same < _Key, std::string >::value >::type >
Value valueFor(char const * key)
{
std::cout << "valueFor(char const * key)\n";
return Value{};
}
};
int main()
{
Map<std::string, int> map;
int v = map.valueFor("my_key");
Map<int, int> other_map;
//int v = other_map.valueFor("my_key"); // BOOM!
}
Just weaken your type requirements. Your valueFor doesn't (need to) care what type the argument is, so long as the expression hash<Key>(arg) is valid.
So, you can template valueFor on its argument type, and just specialise your hash function and if necessary your key comparator.
eg. (untested, and using C++17 string_view for brevity)
template <typename K>
struct Hasher
{
static size_t hash(K const &k) { return std::hash<K>()(k); }
};
template <>
struct Hasher<std::string>
{
static size_t hash(std::string const &s) {
return std::hash<std::string>()(s);
}
static size_t hash(std::string_view const &sv) {
return std::hash<std::string_view>()(sv);
}
static size_t hash(const char *cstr) {
return std::hash<std::string_view>()({cstr});
}
};
template <typename Key, typename Value>
template <typename KeyArg>
Value Map<Key,Value>::valueFor(KeyArg&& arg)
{
auto hash = Hasher<Key>::hash(std::forward<KeyArg>(arg));
// ...
}

template method of functor "can't deduce template parameter"

g++ with std=c++14 is giving me a "couldn't deduce template parameter ‘Key'" error, on a template method of a functor class (that isn't itself a template).
I can't figure out why. The code looks like it should work.
I am implementing a 2 3 tree, and it has a level order traversal method that takes a functor.
operator. The tree23 code is basically this:
template<class Key, class Value> class tree23 {
public:
class Node23 {
friend class tree23<Key, Value>;
public:
// snip...
friend std::ostream& operator<<(std::ostream& ostr, const Node23& node23)
{
// snip... outputs the keys and values of node23 to ostr.
}
private:
Node23 *parent;
std::array<Key, 2> keys;
std::array<Value, 2> values;
std::array<std::unique_ptr<Node23>, 3> children;
int totalItems; // either TwoNodeItems or ThreeNodeItems
// snip...
};
template<typename Functor> void levelOrderTraverse(Functor f) const noexcept;
// snip...
};
The level order traversal invokes the functor's function call operator, passing it two parameters.
template<class Key, class Value> template<typename Functor> \
void tree23<Key, Value>::levelOrderTraverse(Functor f) const noexcept
{
std::queue< std::pair<const Node23*, int> > queue;
Node23 *proot = root.get();
if (proot == nullptr) return;
auto initial_level = 1; // initial, top level is 1, the root.
queue.push(std::make_pair(proot, initial_level));
while (!queue.empty()) {
std::pair<const Node23 *, int> pair_ = queue.front();
const Node23 *current = pair_.first;
int current_tree_level = pair_.second;
// invokes functor's operator()(const Node23&, int)?
f(*current, current_tree_level);
if (!current->isLeaf()) {
for(auto i = 0; i < current->getChildCount(); ++i) {
queue.push(std::make_pair(current->children[i].get(), current_tree_level + 1));
}
}
queue.pop();
}
}
The functor is quite simple:
class levelOrderPrinter {
private:
// snip...
public:
levelOrderPrinter(std::ostream& ostr_lhs, int depth);
levelOrderPrinter(levelOrderPrinter&);
levelOrderPrinter(const levelOrderPrinter&);
template<class Key, class Value>
void operator()(const typename tree23<Key, Value>::Node23& node,
int current_level) noexcept;
};
template<class Key, class Value>
void levelOrderPrinter::operator()(const typename tree23<Key, Value>::Node23& node,
int current_level) noexcept
{
// Did level change?
if (level != current_level) {
level = current_level;
ostr << "\n\n" << "level = " << level;
// Provide some basic spacing to tree appearance.
std::size_t num = tree_depth - level + 1;
std::string str( num, ' ');
ostr << str;
}
ostr << node;
}
If you change the declaration of your function call operator like so:
template<class Node>
void levelOrderPrinter::operator()(const Node& node, int current_level) noexcept
Then the compiler will be able to deduce the type of Node.
In your original code:
template<class Key, class Value>
void levelOrderPrinter::operator()(const typename tree23<Key, Value>::Node23& node,
int current_level) noexcept
The compiler is not able to deduce the types Key or Value because tree23<Key, Value>::Node23 is a non-deduced context for Key and Value (§14.8.2.5), forcing you to use the explicit function template call syntax.
Changing this line
f(*current, current_tree_level);
in the method
template<class Key, class Value> template<typename Functor> \
void tree23<Key, Value>::levelOrderTraverse(Functor f) const noexcept
to be
f.template operator()<Key, Value>(*current, current_tree_level);
got rid of the "can't deduce template parameter” error. It isn't pretty but it now compiles.
Node23 is a nested type, and template parameters of its enclosing type cannot be deduced that easily. So you've had to specify them explicitly via template operator().

using C++ templates to switch strategies / algorithms

I have a C++ class that might work with linear or binary search.
Currently I have additional member variable that show what search to be used.
I know how I can do the search with Java-like virtual functions (strategy pattern or template method),
but I am curious what is best practice this to be done compile time with templates<>?
Current (simplified) code looks like this:
int MyArray::lookup(const String &key) const{
if (lookupMethod == LINEAR_SEARCH)
return lookupLinearSearch(key);
else
return lookupBinarySearch(key);
}
The most obvious way is to encapsulate the strategies in types and implement lookup() as a function template:
namespace searchStrategy {
struct linearSearch {
static auto lookup(const MyArray& a, const String &key)
-> int {
return a.lookupLinearSearch(key);
};
};
struct binarySearch {
static auto lookup(const MyArray& a, const String &key)
-> int {
return a.lookupBinarySearch(key);
};
};
}
class MyArray {
public:
template <typename LookupStrategy>
auto lookup(const String &key) const
-> int {
return LookupStrategy::lookup(*this, key);
};
};
// ...
auto myArray = MyArray{};
mArray.lookup<searchStrategy::binarySearch>("asdf"s);
Alternatively you can specialize the template and use the strategy type as tag only:
namespace searchStrategy {
struct linearSearch {};
struct binarySearch {};
}
class MyArray {
public:
template <typename LookupStrategy>
int lookup(const String &key) const;
template <>
int lookup<searchStrategy::linearSearch>(const String &key) const {
return lookupLinearSearch(key);
};
template <>
int lookup<searchStrategy::binarySearch>(const String &key) const {
return lookupBinarySearch(key)
};
};
// ...
auto myArray = MyArray{};
mArray.lookup<searchStrategy::binarySearch>("asdf"s);
Now you might prefer to not have to define the template parameters, but use a function parameter instead. You don't even need templates for that, but just method overloading:
namespace searchStrategy {
struct LinearSearchT {};
struct BinarySearchT {};
static const LinearSearchT linearSearch;
static const BinarySearchT binarySearch;
}
class MyArray {
public:
int lookup(const String &key,
const searchStrategy::linearSearchT strategy) const {
return lookupLinearSearch(key);
};
int lookup(const String &key,
const searchStrategy::binarySearchT strategy) const {
return lookupBinarySearch(key)
};
};
// ...
auto myArray = MyArray{};
mArray.lookup("asdf"s, searchStrategy::binarySearch);
Looks like dynamically choosing the strategy, but is static. You can not do something like this:
mArray.lookup("asdf"s, (someReason) ?
searchStrategy::binarySearch :
searchStrategy::linearSearch);
In that case you'd have to write
if( someReason )
mArray.lookup("asdf"s, searchStrategy::binarySearch);
else
mArray.lookup("asdf"s, searchStrategy::linearSearch);
Beware: all code in this answer is untested. It will contain bugs.
You can use template specialization, provided lookupMethod is known at compile time.
For example, you can do it like this:
template <int METHOD>
int MyArray::lookup(const String& key); // Not implemented
template<>
int MyArray::lookup<LINEAR_SEARCH>(const String& key) {
return lookupLinearSearch(key);
}
template<>
int MyArray::lookup<BINARY_SEARCH>(const String& key) {
return lookupBinarySearch(key);
}
If you are willing to always commit to which strategy you are going to use at compile time, you can do this:
enum class METHOD {LINEAR_SEARCH, BINARY_SEARCH};
template <METHOD M>
int MyArray::lookup(const String & key) {
if (M == METHOD::LINEAR_SEARCH)
return lookupLinearSearch(key);
else
return lookupBinarySearch(key);
}