hana::make_map with lifted template - c++

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.

Related

Why is there no variable template template parameter?

I'm planning to create a variable template that takes (variable) template-template parameter and one typename:
template <template <typename> auto MetaPredicate, typename T>
constexpr bool has_predicate_v_ = requires {
{ MetaPredicate<T> } -> std::convertible_to<bool>;
}
Where the expectations are:
template <typename T>
struct dummy_01 {
inline static constexpr bool value = true;
};
template <typename T>
inline constexpr bool dummy_01_v = dummy_01<T>::value;
std::cout << std::boolalpha << has_predicate_v_<dummy_01_v, int> << '\n'; // true
But that doesn't work. It would be useful if they exist in standard.
Another case is to create a metafunction count_if:
template <typename Type, template <typename> bool Predicate>
struct count_if {
inline static constexpr size_t value = /** ... **/;
};
template <typename Type, template <typename> bool Predicate>
inline constexpr size_t count_if_v = count_if<Type, Predicate>::value;
// ...
count_if_v<std::tuple<int, double, void, size_t, unsigned short>,
std::is_integral_v> // yields to 3
There is also a proposal relating to my question: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2008r0.html
Why are there currently no variable template-template parameters/arguments?
What is the status of the proposal?
Are there any possible alternatives for variable template-template parameters/arguments?
You already linked to the proposal, so you’ve largely answered the question yourself. You might not know about the paper tracker that can answer the “status” question; it says that more motivating examples are sought (which might very well have been delayed by the pandemic), so maybe you should contribute yours!
As for alternatives, the usual one is to key on the trait rather than the helper variable template. Obviously new code might have to wrap a (foundational) variable template with a helper trait class template to take advantage of this, but it works.
To extend #DavisHerring's answer: I think there is little use of it: I don't really see the advantage of using the helper variable template instead of the trait directly. Here is e.g. what I would do for your two examples:
In C++20 I would actually write a concept
template <template <typename> class Predicate, typename T>
concept is_predicate = requires {
std::same_as<decltype(Predicate<T>::value), bool>;
};
that makes sure that a given predicate has a static bool member variable value. Before that you might additionally use std::enable_if instead or not use SFINAE at all.
Example 1:
template <template <typename> class MetaPredicate, typename T>
requires is_predicate<MetaPredicate, T>
constexpr bool has_predicate_v_ = requires {
std::same_as<decltype(MetaPredicate<T>::value), bool>;
};
and then call it with the trait has_predicate_v_<dummy_01, int> instead of the alias.
Try it here
Example 2:
template <template <typename> class Predicate, typename... Ts>
requires (is_predicate<Predicate, Ts> && ...)
struct count_if {
inline static constexpr size_t count = ((Predicate<Ts>::value == true ? 1 : 0) + ...);
};
and again call it with the trait std::is_integral instead: count_if<std::is_integral, int, double>::count.
Try it here

Is there a way to use variable template as a parameter?

I want to declare a function, which will take as a parameter a variable (let's say, int), which should be parametrized by a class. Speaking in terms of lambda calculus, I want my parameter to have a kind * -> int.
Example of a function I want to be able to write (Spec is the variable):
template <??? Specification, typename T>
auto make_array() {
return std::array<T, Specification<T>>;
}
Since C++14 we have variable templates, so we can do something like this:
template <typename T>
constexpr int digits = std::numeric_limits<T>::digits;
The problem is, how do I pass that into a function? In the notes section of cppreference it is stated that
Variable templates cannot be used as template template arguments.
But does that mean that there is actually no way to pass parametrized variable as a function parameter? What you can do is, for example, create a class which has a static field denoting value, but an obvious drawback is that the users of my function must derive from that class.
I believe there might be some workaround using SFINAE, but I lack skills in that area.
Unless you insist on using a variable template, you can use a type trait:
template <typename T> struct Specification;
you can specialize it for example for int:
template <>
struct Specification<int> {
static constexpr size_t value = 42;
};
and as you want to have different Specifications, pass it as template template parameter:
template <template<class> class S, typename T>
auto make_array() {
return std::array<T, S<T>::value>{};
}
Complete example:
#include <array>
#include <cstddef>
template <template<class> class S, typename T>
auto make_array() {
return std::array<T, S<T>::value>{};
}
template <typename T> struct Specification;
template <>
struct Specification<int> {
static constexpr size_t value = 42;
};
int main(){
auto x = make_array<Specification,int>();
}
Note that I was rather verbose for the sake of clarity. You can save a bit of typing by using std::integral_constant, eg:
template <>
struct Specification<double> : std::integral_constant<size_t,3> {};
As an follow-up on idclev's answer, you can avoid the need to explicitly specialise for the different types for individual "specifications" just by inheriting from e.g. integral_constant. For example, your desired usage was something like
template <template <typename T> int Specification, typename T>
auto make_array() {
return std::array<T, Specification<T>>;
}
template <typename T>
constexpr int digits = std::numeric_limits<T>::digits;
// ...
auto foo = make_array<digits, double>();
However, as you have noted, this is impossible: you cannot pass variable templates as template-template parameters. However, by turning digits into a structure directly you can do this:
template <template <typename T> class Specification, typename T>
auto make_array() {
// we no longer have the guarantee of the type here, unfortunately
// but you can use a static_assert to improve error messages
// (also using `std::size_t` here for correctness)
static_assert(
std::is_convertible<decltype(Specification<T>::value), std::size_t>::value,
"value must be convertible to size_t");
return std::array<T, Specification<T>::value>;
}
// use a type rather than a variable template
// we just inherit from integral constant to save on some typing
// (though you could do this explicitly as well)
template <typename T>
struct digits : std::integral_constant<int, std::numeric_limits<T>::digits> {};
// ...
// same call!
auto foo = make_array<digits, double>();

Converting std::type_identity object to a type

Suppose that we create two type_of functions that return std::type_identity, like:
template<auto VAR>
auto type_of() {
return std::type_identity<decltype(VAR)>{};
}
template<typename T>
auto type_of() {
return std::type_identity<T>{};
}
The way to get an actual type from std::type_identity seems a bit cumbersome:
// this works
// both i1 and i2 are ints
decltype(type_of<int>())::type i1;
decltype(type_of<int{}>())::type i2;
Is there a way to waive the need for decltype in above expressions, or to put it inside a reusable expression, to achieve something nicer like:
// can this work?
type_of<int>()::type i1;
type_of<int{}>()::type i2;
Or even better:
// can this work??
type_of_t<int> i1;
type_of_t<int{}> i2;
Note: specialization for type and non-type template parameter, which could have been a direction, doesn't work (cannot compile):
template<auto>
struct type_of;
template<typename T>
struct type_of<T> { // <== compilation error: type/value mismatch
using type = T;
};
template<auto VAR>
struct type_of<VAR> {
using type = decltype(VAR);
};
You can create a type alias. However you can't "overload" it. So my solution is to create two:
template <auto Var>
using type_of_var_t = decltype(type_of<Var>())::type;
template <class T>
using type_of_t = decltype(type_of<T>())::type;
auto test()
{
type_of_var_t<11> i1 = 24;
type_of_t<int> i2 = 17;
}
In C++, a template parameter must be a value, a type, or another template (which itself must fit within the declared template header). It must be exactly one of these.
If you want to do this:
the idea is to get something that is unaware on the caller side whether the template parameter is a type or a variable
The basic requirement for being able to do this is to write a template with a parameter that could be a value or a type.
That's not a thing C++ allows.
Template function overloading allows you to get away with something like that. But that only works because it isn't one template. It's two templates that are overloaded. Which one gets selected depends on the template arguments provided.
Template classes can't be overloaded. And template specialization cannot change the nature of the original template (like what its template parameters are). It can only allow you to reinterpret the template parameters of the original template parameters in order to provide an alternative implementation.
If you want this, you're going to have to wait until either C++ gets the ability to have a template parameter that could be anything or until C++ gets the ability to convert types into values and back (ie: reflection).
Getting the type from an std::type_identity object can be encapsulated into the following expresion:
template<auto x>
using type = typename decltype(x)::type;
This would allow to replace the cumbersome expressions:
decltype(type_of<int>())::type i1;
decltype(type_of<int{}>())::type i2;
With a more simple expression:
type<type_of<int>()> i1;
type<type_of<int{}>()> i2;
It still requires to go through two steps (type_of then type) as the first one shall be able to get a type or a variable, which is applicable only with function template overloading, then the function cannot return a type, so it returns an object that needs a template expression to extract the inner type.
Depending on what you want to do with the type, the code can become even simpler.
If all you want is to create an object of that type, you can forward the creation of the object into the function:
template<auto VAR, typename... Args>
auto create_type_of(Args&&... args) {
return decltype(VAR){std::forward<Args>(args)...};
}
template<typename T, typename... Args>
auto create_type_of(Args&&... args) {
return T{std::forward<Args>(args)...};
}
auto i1 = create_type_of<int>(7);
auto i2 = create_type_of<int{}>(7);
The generic case of creating a type from std::type_identity can work this way:
template<auto VAR>
constexpr auto type_of() {
return std::type_identity<decltype(VAR)>{};
}
template<typename T>
constexpr auto type_of() {
return std::type_identity<T>{};
}
template<typename T, typename... Args>
auto create(std::type_identity<T>, Args&&... args) {
return T{std::forward<Args>(args)...};
}
auto i1 = create(type_of<int>(), 7);
auto i2 = create(type_of<int{}>(), 7);
Note that the entire thing works only with variables that can be used as non-type template parameters. For a more generic approach that works without templates (but with a macro...), and thus can work for variables that cannot be template parameters, see this mind blowing neat answer!
By design, template arguments in C++ templates are either templates, types or values.
Within the template, you know which they are. All expressions within the template that are dependent on the template arguments are disambiguated using typename or template keywords (or context) so their category is known before arguments are substituted.
Now there are metaprogramming libraries that work with value-substitutes for all 3.
template<class T> struct type_tag_t {using type=T;};
template<class T> constexpr type_tag_t<T> tag={};
template<template<class...>class Z> struct template_z_t {
template<class...Ts>
using result = Z<Ts...>;
template<class...Ts>
constexpr result<Ts...> operator()( type_tag_t<Ts>... ) const { return {}; }
};
template<template<class...>class Z>
constexpr template_z_t<Z> template_z = {};
here templates are mapped to constexpr function objects. The template_z template lets you map type-templates over to this domain.
template<auto x>
using type = typename decltype(x)::type;
template<auto x>
constexpr std::integral_constant<std::decay_t<decltype(x)>, x> k = {};
So,
constexpr auto vector_z = template_z<std::vector>;
constexpr auto vector_tag = vector_z( tag<int>, tag<std::allocator<int>> );
then you can go back to the type:
type<vector_tag> v{1,2,3,4};
this probably isn't what you want.
You might be willing to check the proposal for Universal Template Parameters
The implication example quite match your use-case of specializing for both TTP and NTTP :
template <template auto>
struct X;
template <typename T>
struct X<T> {
// T is a type
using type = T;
};
template <auto val>
struct X<val> : std::integral_constant<decltype(val), val> {
// val is an NTTP
};

How can I get access to this nested template parameter?

Here is my code:
template<
template <typename TSEvent,
typename ...TSEvents> typename V,
typename... Filtered>
constexpr auto filter() {
if constexpr(sizeof...(TSEvents) == 0) {
return type_list<Filtered...>{};
}
if constexpr(is_default_constructible<TSEvent>::value) {
return filter<<TSEvents...>, Filtered...>();
}
return filter<<TSEvents...>, Filtered...>();
}
I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?
Usually through another level of indirection, and usually a struct that we can specialize.
For example:
namespace detail
{
template<class...>
struct filter_t;
template<template<class, class...> class V, class TSEvent, class... TSEvents, class... Filtered>
struct filter_t<V<TSEvent,TSEvents...>, Filtered...>
{
static constexpr auto filter() {
return sizeof...(TSEvents);
}
};
} // detail
template<class... T>
constexpr auto filter()
{
return detail::filter_t<T...>::filter();
}
template<class T, class...U>
struct type_list{};
int main()
{
std::cout << filter<type_list<int, int, int>, int>();
}
Live Demo
Just to present another option, you could do this with only functions.
#include <iostream>
using namespace std;
template<typename...>
struct type_list{};
template < template <typename...> typename T,typename A,typename... B, typename... Filtered>
constexpr auto filter_impl(T<A,B...>*,type_list<Filtered...>)
{
using filtered_list = std::conditional_t<is_arithmetic<A>::value,
type_list<Filtered...,A>,
type_list<Filtered...>>;
if constexpr (sizeof...(B) == 0)
return filtered_list();
else
return filter_impl( (T<B...>*)0, filtered_list());
}
template <typename T>
constexpr auto filter()
{
return filter_impl( (T*)0,type_list<>());
}
struct not_arethmetic{};
int main() {
auto b = filter< type_list<not_arethmetic,int,bool,not_arethmetic,double> >();
static_assert(std::is_same< decltype(b) , type_list<int,bool,double>>::value);
return 0;
}
Demo
One thing, In your original example your first if expression will mean that the final TSEvent is not checked, as it returns if the varadic TSEvents... is zero size, but there will be one final element to check whether is_default_constructible.
Also, you might find this post useful regarding template template parameter names.
I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?
Short answer: no.
Long answer: with
template<
template <typename TSEvent,
typename ...TSEvents> typename V,
typename... Filtered>
constexpr auto filter()
you set two template arguments for the filter() function.
The first one, related to the TSEvents variadic list, is a template-template argument that receive one or more types argument.
But your function doesn't receive a type that is based over that template-template (with a fixed TSEvent type and a fixed TSEvents); receive the template-template.
So doesn't make sense the test size...(TSEvents) because, for filter() isn't
fixed the TSEvents list.
To explain this in another way... you can call filter this way
filter<std::tuple, short, int, long>();
Ask for sizeof...(TSEvents) is asking how many types contains std::tuple where std::tuple is only the container of types but without contained types.
If you want to make some sort of actions in your filter() function, you need a type template parameter, not a template-template parameter.
It's simpler with classes (see AndyG's answer) where you can use partial specialization (with functions you can't) or with function when they receive arguments from which you can deduce types.
Suppose your filter() receive an object of type V<SomeTypes...> and an object of type std::tuple<Filtered...>, you can write something as follows (caution: code not tested)
template<
template <typename ...> typename V,
typename TSEvent, typename ... TSEvents, typename... Filtered>
constexpr auto filter (V<TSEvent, TSEvents...> const & v,
std::tuple<Filtered...> const & t) {
/* some code where you can use also TSEvent and TSEvents... */
}
This way TSEvent and TSEvents... are deduced from the v argument.

c++ template interdependant types

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