c++ template interdependant types - c++

I am trying to write simple hashtable in c++. My hashtable implementation template looks like this:
template<class k, class v, class h<k>, class e<k> >
class my_hash {
};
where
k = class type for key
v = class type for value
h = class type for hash fn
e = class type for equality fn
I have defined class h like this
template<class k>
class h {
};
I would specialize above template for different k types e.g. int, string etc. What I want to do is whenever I invoke my_hash template with k,it should automatically pick up the
h<k>
as the hash function type.For this to happen how do I define template ?
If I define it like I have shown it above, g++ gives compiler error saying h is not a template ? Could somebody please help me with this ?

I think what you need is called template template parameter and it is this:
template<class k, class v, template<typename> class h, template<typename> class e>
class my_hash
{
//then here you can intantiate the template template parameter as
h<k> hobj;
e<k> eobj;
//...
};
Now you can pass class template (which takes one type argument) as the third and fourth template argument to the above class template. Look for template template parameter in your book, or online, know more about it. You can start from here:
What are some uses of template template parameters in C++?
C++ Common Knowledge: Template Template Parameters
Hope that helps.

You can certainly use template template parameters, but your intended use case - where the template types are closely related - is a common one, that is idiomatically solved with traits.
With hash keys, usually the key type is closely related with the hash function and equality function. With traits you can do something like this silly example:
template <class T> struct key_hash_traits;
template <>
struct key_hash_traits<int>
{
typedef int key_type;
static size_t hash(int k) { return k*k / 42; }
};
template <class T, class V>
struct my_non_functioning_hash_table
{
void insert(T::key_type t, V v)
{
if (T::hash(t) == 13)
{
std::cout << "hello world\n";
}
}
};
int main()
{
int k = 256;
my_non_functioning_hash_table<key_hash_traits<int>, float> h;
h.insert(k, 3.14);
}
See how with key_hash_traits, all the interrelated types (key, hash func) are placed together, which is nice, and the definition of my_non_functioning_hash_table is simpler too as it only needs to refer to the trait. This example does assume you'll only ever have one hash func per key type, but you can easily modify that. I hope you get the general idea.
For more reading on traits, see these links:
Traits: a new and useful template technique
Traits: The else-if-then of Types

Related

C++ class template with non-type parameter depending on type parameter problem using C++11

I understand that the normal means in C++ of controlling at compile time which class template member functions are included/excluded is through partial specialization. I have a class template which has a type parameter and a non-type parameter depending on the type parameter. When the non-type parameter is a certain value I want certain member functions of a class template to be part of the instantiation of the class template but not others. The normal way of doing this is to code the primary template to include certain member functions and then to code a partial specialization of the primary template with a different set of member functions. But because of the rule which says:
"Non-type template argument cannot specialize a template parameter whose type depends on a parameter of the specialization" I can not do this. Is there a way around this ? Simple example code would look like:
template
<
class T,
T * v
>
struct AClassTemple
{
void SomeMemberFunction() { code etc. }
void AnotherMemberFunction() { code etc. }
};
When 'v' is a nullptr I want my class template to not include AnotherMemberFunction and its code, which assumes a value for 'v' which is not a nullptr. I can not partially specialize the class template by specifying:
template
<
class T
>
struct AClassTemple<T,nullptr>
{
};
etc. else I get a compiler error reflecting the rule above.
The following should do what you want:
#include <type_traits>
template<class T, T *v>
struct AClassTemple {
template<T *w = v, class = std::enable_if_t<w != nullptr>>
void SomeMemberFunction() {}
template<T *w = v, class = std::enable_if_t<w != nullptr>>
void AnotherMemberFunction() {}
};
int main() {
constexpr static char c = '0';
AClassTemple<int, nullptr> a{};
AClassTemple<const char, &c> b{};
// a.SomeMemberFunction(); // will not compile
b.SomeMemberFunction();
}

hana::make_map with lifted template

I'm trying to create a map of Key-Task pairs at compilation time. The key is a sequential number which should also be used as a template parameter inside of the mapped Task types. What I learned is that I need to lift my Task into a metafunction to make that work but I'am already getting problems in creating the right hana::types that match my template parameters.
This is what I have so far:
template <std::size_t Key,
typename T = double,
template<typename...> class Complex = std::complex>
class Task
{
...
}
template <std::size_t Begin,
std::size_t End,
typename T,
template<typename...> class Complex = std::complex>
class TaskFactory
{
static constexpr auto create(void)
{
auto keys = hana::make_range(hana::int_c<Begin>, hana::int_c<End>);
return hana::unpack(keys, [](auto... key)
{
return hana::make_map(hana::make_pair(key, hana::template_<Task>(hana::type_c<key>, hana::type_c<T>, hana::type_c<Complex>)())...);
});
}
static constexpr auto taskMap_ = create();
...
}
int main()
{
TaskFactory<2, 8, double, std::complex> myTaskFactory;
return 0;
}
Clang is complaining with:
error: template template argument has different template parameters than its corresponding template template parameter
What am I doing wrong and is this the right approach?
Best
Wum
hana::template_ only works with typename template parameters - it doesn't support non-type template parameters or template template parameters. See its implementation here.
The same applies for hana::type_c.
hana::type_c<key> is not valid as key is not a type.
hana::type_c<Complex> is not valid as Complex is not a type.
Your approach up to hana::unpack(keys, [](auto... key) looks OK to me. You need to change your Task class to be defined in terms of types - e.g.:
template <typename Key,
typename Complex>
class Task
{
// ...
};
That way, you can use hana::template_ as you intend to.

Implementation of abstract class without template argument

I want to implement two simple abstract classes like so:
class Hashable {
public:
virtual Int hashValue() = 0;
};
template <typename T>
class Equatable {
virtual Bool operator == (const T& other) = 0;
}
These classes will give me the opportunity of partial template specialization in my new dictionary class.
However, I could not make them to work. Here is the declaration of my dictionary class:
template <Hashable Key, typename Value>
class Dictionary {
.
.
.
};
The problem is, key should also be Equatable because hashability should require it.
So, I have two questions:
Can we rewrite Equatable<T> class to have no template arguments? Does C++ have any keyword referring to current type of the class?
In my opinion, Hashable had better inherit from Equatable class. How to achieve this without new template definition on Hashable (if my first question is answered yes, this is already solved then)?
What would be the best object-oriented approach here? To have an interface class with template arguments seems tacky.
Thank you.
What you're basically looking for is Concepts, with which you'd write something like:
template <class T>
concept bool Hashable()
{
return requires(T t, T u) {
{t.hashValue()} -> size_t;
{t == u} -> bool;
};
}
template <Hashable Key, class Value>
class Dictionary {
...
};
But that won't even be in C++17.
Until then, we can write this sort of thing in C++14 using void_t:
template <class...> using void_t = void;
template <class T, class = void>
struct Hashable : std::false_type { };
template <class T>
struct Hashable<T, void_t<
std::enable_if_t<std::is_same<std::declval<T&>().hashValue(), std::size_t>::value>,
decltype(std::declval<T&>() == std::declval<T&>())
>>
: std::true_type { };
template <class Key, class Value>
class Dictionary {
static_assert(Hashable<Key>::value, "Key must be Hashable<>");
...
};
Note that in both cases, we're requiring the Key type to have this functionality - we're not requiring the Key to inherit it virtually. This is far more efficient. No virtual dispatch necessary.
What would be the best object-oriented approach here?
To not use an object-oriented approach.
I believe that
template <Hashable Key, typename Value>
does not actually do what you expect it to do. Consider:
template <int Key, typename Value> class x{};
now, you can instantiate x<1, int> and x<2, int>, but these are not merely different objects, but different types. So, in your case, your Hashable object would become part of the type (so it would have to be generated during compilation, not at runtime).
What you most probably want instead is - like Wojciech Frohmberg mentioned in the other answer:
template <typename K, typename V>
class Dict {
...
static_assert(std::is_base_of<K, Hashable>::value, "Only Hashable can be the key);
}
or enable_if or some other template magic included from type_traits.
What you're looking for are concepts, that didn't even make C++17, or typeclasses (available in other languages, like Haskell or Scala)
If you really want to use object-oriented approach here, go for something like:
template <typename Value>
class Dict {
Dict(std::shared_ptr<Hashable>, Value)
{}
}
However, it's not a typical implementation, so I wouldn't recommend it

template<typename> - how does it work?

I have come across such syntax:
template<typename>
struct is_const{static const bool value = 0;};
How will this code behave and how could it be applied? I didn't find any example or explanation in the Internet.
I am wondering about lack of the argument name (e.g. T).
This is a primary template which takes a single template argument and has a static bool member equal to 0. It is likely that this is the primary template definition for a type trait and that there is a corresponding specialization elsewhere which looks like this:
template <typename T>
struct is_const<const T>
{static const bool value = 1;};
This would allow you to check if a type is const qualified like so:
static_assert(!is_const<int>::value, "wat");
static_assert(is_const<const int>::value, "wat");
template <typename T>
class SampleClass
{
public:
T values [2];
SampleClass(T first, T second)
{
values[0]=first;
values[1]=second;
}
};
Try Something like this typename are generic way of programming. search for generic programming c++. You will get alot of resources
On a basic level of explanation, each time the template is used the compiler generates a version of the templated struct, class, function, etc.
for example:
template<typename T>
struct StructName
{
T memberVariable;
};
when this code is used:
StructName<float> variable = StructName<float>();
the compiler generates:
struct StructName
{
float memberVariable;
};
you can read more about this here: https://en.wikipedia.org/wiki/Template_metaprogramming

template class multiple definition

I have a simple hash table template,
template <class K, class V, long H(K)>
class HashTableAbs{/* .... */}
//where K is key type, V is value type, H gives hash code for K value
and simple inheritor class
template <class K, class V, long H(K)>
class HashTable:public HashTableAbs<K, V, H> {};
and i want to specialize this template for string, with my default hash function
template <class V>
class HashTable<std::string, V, strSimpleHash>:public HashTableAbs<std::string, V, strSimpleHash> {};
//where strSimpleHash is long strSimpleHash(std::string)
But when i trying to compile this compiler write this
test.o: In function `strSimpleHash(std::string)':
test.cpp:(.text+0x0): multiple definition of `strSimpleHash(std::string)'
/tmp/main-i7yPhc.o:main.cc:(.text+0x0): first defined here
clang: error: linker command failed with exit code 1 (use -v to see invocation)
(test includes hashtable.h where HashTable is defined)
strSimpleHash is defined only in hashtable.h
Is there way out?
PS sorry for my writing mistakes. English in not my native language
This error is not really related to templates. It is a multiple definition of a function. I guess you have defined strSimpleHash in a header without using inline (you didn't add the code where you define this function).
You asked in your comments a way to use HashTable like the following:
HashTable<std::string, int> // (ie, without passing the 3rd argument)
This is not directly possible. Althought you can specify a default value for a template argument (but not in a specialization), for example:
template<int n = 0> struct dummy {};
dummy<> obj;
in your case you cannot do that, because the general case of your template does not accept only functions of type long(std::string).
However, it is possible to workaround this by assigning a default function for each possible type. Note that you should use pointer-to-function, as the code is a little clearer:
// A C++03-style template typedef
template<typename K>
struct HashFunc
{
typedef long (*type)(K);
};
// The default value. Defined only for some cases,
// compile error for not handled cases
template<typename K>
struct DefaultHash;
template<>
struct DefaultHash<std::string>
{
// Static constant pointer-to-function, pointing to the hash func.
static constexpr HashFunc<std::string>::type func = &strSimpleHash;
};
// The HashTable class
template <class K, class V, typename HashFunc<K>::type H = DefaultHash<K>::func>
class HashTable
{
};
template <class V>
class HashTable<std::string, V, strSimpleHash>
{
};
And then you can use it as you wanted, omitting the third template parameter. Note that this code compiles on gcc, but not on clang. (Actually, I'm not sure about which compiler is right...)