Today, while using the same template for the hundredth time, I came across the idea to introduce a simple replacement.
Instead of
QHash<int,QHash<int,QHash<int,int> > >
it should now be:
MultiKeyHash<int,int,int,int>
(Not to be confused with QT's QMultiHash class).
Whereas the first three arguments are the types of the keys and the last argument is the type of the value.
MultiKeyHash<TKey1,TKey2,....,TKeyN,TValue>
My current implementation looks like this:
template <typename... Args>
struct MultiKeyHash;
template <typename TKey1, typename... Args>
struct MultiKeyHash<TKey1, Args...> : QHash<TKey1,MultiKeyHash<Args...> > {
typedef typename MultiKeyHash<Args...>::ValueType ValueType;
const ValueType& value(const TKey1 &key1, const Args_Without_Last&... args) const {
return operator[](key1).value(args...);
}
};
template <typename TKeyN, typename TValue>
struct MultiKeyHash<TKeyN,TValue> : QHash<TKeyN,TValue> {
typedef TValue ValueType;
};
Everything worked as expected until I wanted to add the "value" method. I certainly can't use "Args" as Type for the second parameter(-pack). Do I have to make a function template of it, or is there a way around?
How do I have to define the "value" method in order to be able to call it like:
value(key1,key2,...,keyn)
Here's working version of MultiHashKey. It uses a dummy implementation of QHash for testing purposes.
#include <iostream>
// Dummy implementation of QHash for testing purposes.
template <typename TKey, typename TValue>
struct QHash
{
void insert(const TKey& key, const TValue& val) {this->val = val;}
const TValue& value(const TKey& key) const { return val; }
TValue val;
};
template <typename... Args> struct MultiKeyHash;
template <typename TKey, typename... Args>
struct MultiKeyHash<TKey, Args...> : QHash<TKey, MultiKeyHash<Args...>>
{
typedef TKey KeyType;
typedef typename MultiKeyHash<Args...>::ValueType ValueType;
typedef QHash<KeyType, MultiKeyHash<Args...>> QHashType;
using QHashType::insert;
using QHashType::value;
MultiKeyHash() {}
MultiKeyHash(const TKey &key, const Args&... args)
{
this->insert(key, MultiKeyHash<Args...>(args...));
}
void insert(const TKey &key, const Args&... args)
{
MultiKeyHash<Args...> val(args...);
this->insert(key, val);
}
template <typename TKey1, typename ... Args1>
const ValueType& value(const TKey1 &key1, const Args1&... args1) const
{
MultiKeyHash<Args...> const& val = this->value(key1);
return val.value(args1...);
}
};
template <typename TKey, typename TValue>
struct MultiKeyHash<TKey, TValue> : QHash<TKey, TValue>
{
typedef TKey KeyType;
typedef TValue ValueType;
typedef QHash<KeyType, ValueType> QHashType;
MultiKeyHash() {}
MultiKeyHash(const TKey &key, const TValue& val)
{
this->insert(key, val);
}
};
void test1()
{
MultiKeyHash<int, double> hash;
hash.insert(10, 200.5);
double v = hash.value(10);
std::cout << "Value: " << v << std::endl;
}
void test3()
{
MultiKeyHash<int, int, int, double> hash;
hash.insert(10, 10, 10, 20.5);
double v = hash.value(10, 10, 10);
std::cout << "Value: " << v << std::endl;
}
int main()
{
test1();
test3();
return 0;
};
Output of running the program:
Value: 200.5 Value: 20.5
If you make value a variadic template, you avoid the problem altogether at the cost of nastier error messages since conversions will happen inside the function instead of at the callsite:
template <typename First, typename...Remainder>
const ValueType& value(First&& first, Remainder&&... rest) const {
return value(std::forward<First>(first)).value(std::forward<Remainder>(rest)...);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With