Adding methods with template specialization - c++

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));
// ...
}

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;
};

Deferrenced pointer to hash a compile time string

I'm trying to implement some kind of map (a tuple of pair) which use compile time string as key (first element of the pair). So I wanted to use this answer but there is a problem with my code : the string is inside a pair.
#include <type_traits>
#include <tuple>
namespace meta {
template < typename T >
struct CType { using type = T; };
namespace detail {
template <typename T>
struct typeid_t {
using type = typename std::remove_cv<
typename std::remove_reference<T>::type
>::type;
};
}
template <typename T>
constexpr decltype(auto) typeid_(T&&) {
return CType<typename detail::typeid_t<T>::type>{};
}
}
struct HashConstString {
using value_type = uint32_t;
static constexpr uint32_t hash(const char* str) {
return str[0];
}
};
template < typename T_Hash,
typename... T_Pairs >
class UniversalMap {
template < typename T_Pair >
using U_Pair = decltype(std::make_pair(
std::integral_constant<typename T_Hash::value_type, T_Hash::hash(std::get<0>(T_Pair{}))>{},
typename decltype(meta::typeid_(std::get<1>(T_Pair{})))::type {}
));
using U_Map = decltype(std::make_tuple(
U_Pair<T_Pairs>{}...
));
private:
U_Map m_map;
};
template < typename T_Hash,
typename... T_Pairs >
constexpr decltype(auto) make_UniversalMap(T_Hash hash, T_Pairs... pairs) {
(void)hash;
((void)pairs,...);
return UniversalMap<T_Hash, T_Pairs...>();
}
int main() {
constexpr auto hashValue = HashConstString::hash("Test");
constexpr auto map = make_UniversalMap(HashConstString{},
std::make_pair("Test", meta::CType<int>{})
);
}
Wandbox
So I don't know how to hash correctly the string when it's already inside the pair. Because std::get give me back a reference and it seems it's the reason why I have a dereferenced null pointer error.
Is there some "tricks" to get this work without having to compute the hash before creating the pair?
The problem is not with std::get but with the fact that you create a tuple of const char*. "Test" decays to const char* when passed as argument to make_pair. Unfortunately explicitly specifying the pair template parameters (e.g. std::pair<const char[5], int>) does not work because you can't create a std container of type array.
The rather awkward solution is to use std::array:
struct HashConstString
{
using value_type = uint32_t;
static constexpr uint32_t hash(const char *str) { return str[0]; }
// add this overload
template <std::size_t N>
static constexpr uint32_t hash(std::array<char, N> str) { return str[0]; }
};
and then call like this:
constexpr auto map = make_UniversalMap(HashConstString{},
std::make_pair(std::array<char, 5>{"Test"}, int{}));
To avoid specifying the size for std::array you can create a helper function:
template <std::size_t N> constexpr auto make_strarray(const char(&str)[N])
{
// unfortunately std::array<char, N>{str} does not work :(
std::array<char, N> arr{};
for (std::size_t i = 0; i < N; ++i)
arr[i] = str[i];
return arr;
}
Or since in C++20 it looks like std::copy will be made constexpr:
template <std::size_t N> constexpr auto make_strarray(const char(&str)[N])
{
std::array<char, N> arr{};
std::copy(str, str + N, arr.begin());
return arr;
}

Specialization of class for vectors of specific type_trait

I'm trying to specialize hash to include std::vector for all arithmetic types, but it's throwing a few errors
./includes/helpers.hpp:14:22: error: default template argument in a class template partial specialization
typename = std::enable_if_t<std::is_arithmetic<dtype>::value> >
^
./includes/helpers.hpp:16:8: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
struct hash<std::vector<dtype> >
^~~~~~~~~~~~~~~~~~~~~~~~~
I tried to follow as close as I could with different enable_if_t guides. But it doesn't seem to be working, what am I doing wrong?
It seems to work without using enable_if_t. But then there would be a possible conflict with vectors that shouldn't use this hash
This is my code so far (editted to be more "complete")
#include <iostream>
#include <type_traits>
#include <vector>
namespace std {
template <typename dtype,
typename = std::enable_if_t< std::is_arithmetic<dtype>::value> >
struct hash<std::vector<dtype> > {
size_t operator()(const std::vector<dtype> &input)
{
//perform hash
}
};
}
using namespace std;
int main()
{
const vector<int> i{1,2,3,4};
cout << hash<vector<int>>()(i) << endl;
return 0;
}
The problem is, that std::hash has only a single template parameter and you cannot add an additional defaulted template parameter in a partial specialization. So you have several choices, depending on what you want to do with your hash.
Please also rethink your approach. This comment by Yakk is very useful:
You may not specialize templates in std unless the specialization depends on a user-provided type. – Yakk
You can easily fix thisby not putting your own hash into the std namespace.
You could simply remove the enable_if from the template argument list and replace it with a
static_assert(std::is_arithmetic<dtype>::value, "!");
in the struct body, given that you only ever want to hash vectors of arithmetic type.
SFINAE on the call operator. This way, you have to provide all the hash methods for all other vector types within the same struct. Also you have to go through some funny business of repeating the template parameter to make the compiler happy. It's very important that your SFINAE criteria are mutually exclusive, otherwise you'll get horrible errors.
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>
namespace std {
template< typename dtype >
struct hash< std::vector<dtype> >
{
template< typename T = dtype >
std::enable_if_t<std::is_arithmetic<T>::value, size_t>
operator()(const std::vector<T> &input) const
{
constexpr size_t FNV_prime = 1099511628211ul;
constexpr size_t FNV_offset = 14695981039346656037ul;
size_t hashed = FNV_offset;
for(const auto &n:input)
{
hashed ^= n;
hashed *= FNV_prime;
}
return hashed;
}
template< typename T = dtype >
std::enable_if_t<!std::is_arithmetic<T>::value, size_t>
operator()(const std::vector<T> &input) const
{
std::cout << "No hash for you :-(\n";
return 0;
}
};
} // namespace std
int main() {
{
std::vector<int> v{1,2,3,4};
size_t hash = std::hash<std::vector<int>>{}(v);
std::cout << hash << "\n";
}
{
std::vector<std::string> v{"Hello", "world!"};
size_t hash = std::hash<std::vector<std::string>>{}(v);
std::cout << hash << "\n";
}
}
Live example
You can also declare your own struct for hashing and add as many template parameters as you want. Then you just need std::hash to inherit from your custom struct.
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>
template < typename T, bool = std::is_arithmetic<T>::value >
struct vector_hash;
template < typename T>
struct vector_hash<T,true> {
size_t operator()(std::vector<T> const &input) const
{
constexpr size_t FNV_prime = 1099511628211ul;
constexpr size_t FNV_offset = 14695981039346656037ul;
size_t hashed = FNV_offset;
for(const auto &n:input)
{
hashed ^= n;
hashed *= FNV_prime;
}
return hashed;
}
};
template < typename T>
struct vector_hash<T,false> {
size_t operator()(std::vector<T> const &) const
{
std::cout << "No hash for you :-(\n";
return 0;
}
};
namespace std {
template< typename dtype >
struct hash< std::vector<dtype> > : vector_hash<dtype> {};
} // namespace std
int main() {
{
std::vector<int> v{1,2,3,4};
size_t hash = std::hash<std::vector<int>>{}(v);
std::cout << hash << "\n";
}
{
std::vector<std::string> v{"Hello", "world!"};
size_t hash = std::hash<std::vector<std::string>>{}(v);
std::cout << hash << "\n";
}
}
Live example
It is illegal to specialize a template in namespace std, unless you do it on a user provuded type. Vector is not user provided.
What you need to do is:
namespace helper{
template<class T, class=void>
struct hash:std::hash<T>{};
}
now you can do usual sfinae tricks with helper's hash, or extend it willy-nilly.
namespace helper {
template <typename dtype>
struct hash<std::vector<dtype>,
std::enable_if_t< std::is_arithmetic<dtype>::value>
> {
size_t operator()(const std::vector<dtype> &input) const {
//perform hash
}
};
}
Simply pass helper::hash<T> in place of std hash. The extra void defaulted parameter permits sfina specialization, the base spec forwards std hash, and there is no ill-formedness issues.

User-defined hash function for unordered_map

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;
};

Get function with generic return type

I try to implement a data structure that comprises multiple name-value pairs where values may differ in their type:
template< typename T >
struct name_value_pair
{
std::string name;
T value;
};
template< typename... Ts >
class tuple_of_name_value_pairs
{
public:
/* type of value */ get_value( std::string n )
{
// return the value that the element in
// _name_value_pairs with name "n" comprises
}
private:
std::tuple<Ts...> _name_value_pairs:
};
Unfortunately, I have no idea how to implement the get function.
A workaround would be to state names as integers instead of strings and use an implementation according to std::get but this no option here: the input type of get has to be a string.
Has anyone an idea?
Firstly have in mind you cannot do what you want directly. C++ is a strongly typed language so type of function result must be known at compile time. So of course if the string you pass to the getter is known at runtime you're not able to dispatch function at compile time to let compiler deduce appropriate result type. But when you accept that you need type-erasure to erase the getter result type you could make use of e.g. boost::variant to deal with your problem. C++14 example (using boost, since c++17 variant should be available in std):
#include <boost/variant.hpp>
#include <utility>
#include <iostream>
#include <tuple>
template< typename T >
struct name_value_pair
{
using type = T;
std::string name;
T value;
};
template <std::size_t N, class = std::make_index_sequence<N>>
struct getter;
template <std::size_t N, std::size_t... Is>
struct getter<N, std::index_sequence<Is...>> {
template <class Val, class Res>
void setRes(Val &val, Res &res, std::string &s) {
if (val.name == s)
res = val.value;
}
template <class Tup>
auto operator()(Tup &tuple_vals, std::string &s) {
boost::variant<typename std::tuple_element<Is, Tup>::type::type...> result;
int helper[] = { (setRes(std::get<Is>(tuple_vals), result, s), 1)... };
(void)helper;
return result;
}
};
template <std::size_t N, class = std::make_index_sequence<N>>
struct setter;
template <std::size_t N, std::size_t... Is>
struct setter<N, std::index_sequence<Is...>> {
template <class Val, class SVal>
std::enable_if_t<!std::is_same<SVal, typename Val::type>::value> setVal(Val &, std::string &, const SVal &) { }
template <class Val>
void setVal(Val &val, std::string &s, const typename Val::type &sval) {
if (val.name == s)
val.value = sval;
}
template <class Tup, class Val>
auto operator()(Tup &tuple_vals, std::string &s, const Val &val) {
int helper[] = { (setVal(std::get<Is>(tuple_vals), s, val), 1)... };
(void)helper;
}
};
template <class T, class Res>
using typer = Res;
template< typename... Ts >
class tuple_of_name_value_pairs
{
public:
auto get_value( std::string n )
{
return getter<sizeof...(Ts)>{}(_name_value_pairs, n);
}
template <class T>
void set_value( std::string n, const T& value) {
setter<sizeof...(Ts)>{}(_name_value_pairs, n , value);
}
void set_names(typer<Ts, std::string>... names) {
_name_value_pairs = std::make_tuple(name_value_pair<Ts>{names, Ts{}}...);
}
private:
std::tuple<name_value_pair<Ts>...> _name_value_pairs;
};
int main() {
tuple_of_name_value_pairs<int, float, double> t;
t.set_names("abc", "def", "ghi");
t.set_value("abc", 1);
t.set_value("def", 4.5f);
t.set_value("ghi", 5.0);
std::cout << t.get_value("def") << std::endl;
}
[live demo]
I'm sure you'll be able to optimise the code (e.g. make use of move semantics/perfect forwarding, etc.). This is only to present you how to get your implementation started.