Detecting actual arity of template template argument - c++

I'm fiddling around with template metaprogramming, specifically with type sequences and STL-like algorithms working on those sequences. One thing I ran into was the transformation of predicates, for example by binding one of their parameters
I think it's hard to describe my problem without first providing some background information. Here's an example:
#include <type_traits>
// Type sequence
template<class... T>
struct typeseq
{
static constexpr size_t Size = sizeof...(T);
};
// Some algorithm
template<class TS, template<class...> class P>
using remove_if = /* ... some template logic ... */;
// Usage example:
using a = typeseq<int, float, char*, double*>;
using b = remove_if<a, std::is_pointer>; // b is typeseq<int, float>
As shown here, remove_if requires a predicate that serves as an oracle for the removal algorithm to determine which of the elements to remove. As we're dealing with metaprogramming, it is in the form of a template template parameter. ( Note that P is using a variadic template parameter here. Although I'm expecting a unary template, an earlier version of C++ had the restriction that a variadic template argument can't be used as a non-variadic template parameter, which severely restricts predicate transformations. ) It requires that the predicate, when instantiated, resolves to a type that has a nested compile time value member that is convertible to bool.
All is well, but say you want to remove every type that is convertible to int. Obviously, std::is_convertible is a binary predicate, but remove_if above requires a unary predicate. We just need to fix the second template argument to int. Let's define a bind2nd:
template<template<class, class...> class P, class BIND>
struct bind2nd
{
template<class T1, class... Tn> using type = P<T1, BIND, Tn...>;
};
// and now we should be able to do:
using c = remove_if<ts, bind2nd<std::is_convertible, int>::type>; // c is typeseq<char*, double*>;
Unfortunately, this fails to compile on latest Clang and MSVC++. Apparently, the issue is the pack expansion of Tn... into std::is_convertible<T1, BIND, Tn...> while std::is_convertible only has 2 template parameters. It doesn't seem to matter that the pack is empty in practice (isolated example)
I'd rather not provide 'overloads' for any required arity of the predicate passed into bind2nd. Is there a way to detect the arity of P in bind2nd above? GCC allows me to partially specialize it for non-variadic versions of P:
template<template<class, class> class P, class BIND>
struct bind2nd<P, BIND>
{
template<class T1> using type = P<T1, BIND>;
};
But unfortunately GCC wasn't the one that was complaining in the first place. I also doubt how conformant such a partial specialization is. Is there a way around this? Is it possible to detect the actual arity of a template template parameter, and do something different based on that information?

I think I found a workaround.
The problem seems related to type aliases - they seem to directly pass along any template arguments, rather than instantiating the type as is the case with a class or struct. We can use this in our favor, by using a struct as an intermediate step:
template<template<class, class...> class P, class BIND>
struct bind2nd
{
template<class... Tn>
struct impl
{
using type = P<Tn...>;
};
template<class T1, class... Tn> using type = typename impl<T1, BIND, Tn...>::type;
};
Now it works :). It's a bit convoluted though. I wonder if this is all according to standard, but it seems to compile on all major compilers.
Edit: Why am I complicating things by using nested types and aliases? I can just as well use derivation for predicates:
template<template<class, class...> class P, class BIND>
struct bind2nd
{
template<class T1, class... Tn>
struct type : P<T1, BIND, Tn...> { };
};
Clean and simple. And it makes it almost identical to the first definition of bind2nd in the OP.

Related

Find the type of a non-type template parameter, based on a template class

Given a class of the form:
template <int A, int B, int C>
struct Functor {
static int go() {
return A*B*C;
}
};
I need to produce a parameter pack / tuple / etc of the types of the parameters for Functor. That is, I want to be able to do things like:
// Imagining that I have many Functor classes...
using FirstArgType = TypeAt<Functor, 1>::T;
FirstArgType t {4};
Essentially, I need to go from a parameter pack of values, to a parameter pack of the TYPES of those values of an unspecialized template class - that is, Functor and not Functor<1, 2, 3>. I naively starting with things that looked like:
template <template <auto...Values> typename Class>
struct ClassInfo {
using Tuple = std::tuple<decltype(Values)...>;
};
However, nested template-template parameters can't be accessed like this (error: use of undeclared identifier 'Values'). Note that when I use auto...Values as top-level template parameters, this tuple technique works fine to discover the types - the problem is in extracting the template params for Class.
For every formulation I've tried, I need to at some point specify a fully specialized type (e.g. Functor<1, 2, 3>) in order to figure out the types - but I am trying to operate on the template class Functor, not the specialization of it Functor<n,n,n> - I need template code to operate on every specialization, e.g. Functor<1, 2, 3> and Functor<4, 5, 6>, not just find the types of a specific specialization.
On one hand: I feel like I'm attempting something fundamentally impossible with C++ templates - in way that I don't understand - which is why every formulation I can think of fails.
On the other hand: CLEARLY the types of the template params for Functor are well-known at compile time, so I imagine there SHOULD be a way to discover these.
A solution would be great, but I'm equally happy to hear about strategies/techniques/design patterns for dealing with template-template parameters that I'm not familiar with (I wouldn't consider myself a pro here).
Essentially, I need to go from a parameter pack of values, to a
parameter pack of the TYPES of those values.
You can use template partial specialization to extract the type of non-type template parameter, something like this:
#include <tuple>
template<auto... args>
struct Functor {};
template <class>
struct ClassInfo {};
template <auto... args>
struct ClassInfo<Functor<args...>> {
using type = std::tuple<decltype(args)...>;
};
using F = Functor<0, 42u, 'a', true>;
static_assert(
std::is_same_v<ClassInfo<F>::type, std::tuple<int, unsigned, char, bool>>);
Demo.
Do you mean something like this?
#include <tuple>
template <auto... Values>
struct GenericFunctor {
using Tuple = std::tuple<decltype(Values)...>;
};
using SpecificFunctor = GenericFunctor<short(1), long(2)>;
// Extracts specific template types from functors.
template<class Functor, int i>
using ArgType = decltype(std::get<i>( std::declval<typename Functor::Tuple>() ));
// Instantiate the specific arg type.
static ArgType<SpecificFunctor, 1> t { 1 };

How to get at a C++ Container<T>'s T if no Container::value_type is provided?

It's rather common that container templates contain a value_type typedef. This makes it easy to create other templated code, most recently concepts, which are able to extract the T if only having been given Container and not Container<T>.
However, not all containers (or other templated classes) define such a value_type, especially older ones.
Is it possible to get to the contained T even without it?
I know there are tricks like "if it is an iterator then .begin() should return a value of that type", but that does not help us to e.g. write a concept-requirement that checks whether a class's .begin() indeed follows the requirements that iterators have.
Here's a solution that's similar to Quimby's solution but using partial template specialization instead:
template<typename...>
struct inner_type_impl;
template<template<typename...> typename C, typename T, typename...Args>
struct inner_type_impl<C<T,Args...>>
{
using type = T;
};
template<typename T>
using inner_type = typename inner_type_impl<T>::type;
Here's a demo that's borrowed from Quimby's solution.
Class template specialization or template argument deduction can be used to implement this. Something like the following should work as long as the inner type is the first template argument:
#include <type_traits>
// Consider the first template argument to be the inner type.
template<template<typename,typename...>class C,typename T,typename...Args>
auto inner_type_impl(const C<T,Args...>* v)->T*{return nullptr;};
template<typename T>
using inner_type = std::remove_pointer_t<decltype(inner_type_impl((T*)nullptr))>;
template<typename T>
struct Container;
// Will still deduce T
template<typename T,typename...Extras>
struct ContainerExtraParams;
static_assert(std::is_same_v<int,inner_type<Container<int>>>);
static_assert(std::is_same_v<int,inner_type<ContainerExtraParams<int,double,float>>>);
I use pointers to make the code valid in evaluated contexts too. Contrary to a possible solution involving std::declval.

How to delete types before T in a parameter pack?

For educational purposes (while reading book Modern C++ Design - A Alexandrescu) I want to write some helper to delete all types in parameter pack before type T.
For example delete_until_T<C,A,B,C,D> must be opened like tuple<C,D> or just <C,D>.
As I understand it may be done in old enough recursive way and non recursive way with C++17.
In this piece of code i wanna do it in non recursive way using std::conditional.
//https://stackoverflow.com/a/23863962/12575933 - based on this answer
#include <tuple>
#include <type_traits>
template<typename...Ts>
using tuple_cat_t = decltype(std::tuple_cat(std::declval<Ts>()...));
template<class T, class ... Ts>
using delete_untill_T = tuple_cat_t
<
typename std::conditional
<
std::is_same_v<T,Ts>||bool_func<>,
std::tuple<Ts>,
std::tuple<>
>::type...
> ;
The first question is how to make a variable or function or struct which value can be changed during this parameter pack opening and used in std::conditional? I want to make bool_func<> which returns false before std::is_same returns true while opening, and after that returns false.
If it's possible, my second question is how to remove tuple and pass just parameters in parameter pack to template function.
Or may be there is another way to delete types before T? (recursive algorithm will do). Or with C++ 20 features?
One way of implementing this is to use partial specializations to match when the type that you're looking for is found in the variadic parameters.
// recursive case, first 2 template parameters don't match
template<typename T, typename, typename ...Ts>
struct delete_until_impl : delete_until_impl<T, Ts...> {};
// base case, first 2 parameters match
template<typename T, typename ...Ts>
struct delete_until_impl<T, T, Ts...>
{
using type = std::tuple<T, Ts...>;
};
// convenience alias to avoid having to say typename everywhere
template<typename ...Ts>
using delete_until_T = typename delete_until_impl<Ts...>::type;
Here's a demo.

how can a c++ concept combine concepts?

I have inherited the following:
template <typename T>
concept IsAwaiter = requires {
typename T::await_ready;
typename T::await_suspend;
typename T::await_resume;
};
template <typename ...AWAITABLES>
concept IsAwaitables = typename std::conjunction<IsAwaiter<AWAITABLES>...>::type;
Building this with clang 10.0.0 results in the following error:
IsAwaiter.h:43:50: error: template argument for template type parameter must be a type
Perhaps just a simple syntax issue, but I've found it hard to find an example which shows how to create a concept based on a variadic template concept parameter.
Any help appreciated!
std::conjunction is for type traits. std::conjunction<IsAwaiter<AWAITABLES>...> is a type with a member static bool value = (IsAwaiter<AWAITABLES>::value && ...), where each IsAwaiter<AWAITABLES> is itself expected to be a type trait with its own static bool member value. This is nonsensical when IsAwaiter<AWAITABLES> is a concept, because concepts are not type traits. They are "just" booleans. Use a fold expression.
template <typename... AWAITABLES>
concept IsAwaitables = (IsAwaiter<AWAITABLES> && ...);
That's it.
struct Dummy {
using await_ready = Dummy;
using await_suspend = Dummy;
using await_resume = Dummy;
};
int main() {
static_assert(IsAwaitables<>);
static_assert(IsAwaitables<Dummy>);
static_assert(IsAwaitables<Dummy, Dummy>);
}
As HTNW points out, you want:
template <typename ...T>
concept IsAwaitables = (IsAwaiter<T> && ...);
But really, do you even need this concept at all? You can just use IsAwaiter directly. And it probably should just be named Awaiter - the typical convention for concepts is to name them as nouns rather than questions (e.g. Range vs IsRange).
If you're taking a parameter pack, you would want to use it anyway:
template <Awaiter... T>
void f(T... awaiters);
and the same with the abbreviated function template syntax:
void f(Awaiter auto... awaiters);
or if you have a fixed number of them, it especially doesn't make sense:
template <Awaiter T, Awaiter U>
void f(T, U);
And even in other contexts where it doesn't neatly fit, it seems better to just manually use Awaiter with a fold-expression. So I question the need for the conjunction concept to begin with.

Catching functors using SFINAE in structure partial specialisation

For some complicated reason, I want to convert any supported type T (coming from a template) to a list of types I have chosen. For this, I tried using a template structure named "Convert". For example:
Convert<short>::type should be int
Convert<int>::type should be int
Convert<char>::type should be int
Convert<float>::type should be double
Convert<double>::type should be double
Convert<const char*>::type should be std::string
Convert<std::string>::type should be std::string
etc.
Those above are easy to implement using template specialisation. But there is one case that is causing problems :
Convert<T>::type where T is a functor should be T
To handle this, I think I have to use SFINAE but I can't manage to make it compile.
The code below gives me "partial specialization cannot match argument list for primary template" (ie. writing "Convert" is forbidden) :
template<typename T, typename = decltype(&T::operator())>
struct Convert<T> { typedef T type; };
And this one gives me "template parameter not used or deducible in partial specialization" (ie. it thinks that T is not used) :
template<typename T>
struct Convert<typename std::enable_if<std::is_function<typename T::operator()>::value,T>::type>
{ typedef T type; };
I have no idea of what to do, all my attempts result in one of the two errors above.
EDIT: I would like to catch other generic things using the same model, so I can't just write "typedef T type" in the non-specialized structure.
Thanks
I'd suggest you use the first approach, but to make it work, you'll have to
Declare the master template with one unused template-argument:
template <class T, class = void> Convert;
Add a void parameter to all specializations of the template you use now.
Define your "functor specialization" like this:
template<typename T, typename std::enable_if<std::is_function<typename T::operator()>::value,void>::type>
That means you make the second argument void if it is a functor (so it matches the default template argument) or nonexisting if it isn't.
BTW why do you use typename in typename T::operator()? AFAIK, operator() is not a type.