Passing variadic template arguments to another template - c++

I have this code in which static_for can be used as a compile-time loop using template-metaprogramming and loop_types is a struct accepting variadic arguments.
Whenever I try to call the function call, it give me an error.
In loop_types, the is a template loop contain the function that prints name of the types.
//Template specialization for getting an index of variadic template
template <unsigned long long index, typename... P>
struct variadic_type_template_index;
template <typename T,typename... P>
struct variadic_type_template_index<0,T,P...>
{
using type = T;
};
template <unsigned long long index,typename T,typename... P>
struct variadic_type_template_index<index,T,P...>
{
using type = variadic_type_template_index<index-1,P...>::type;
};
template <unsigned long long index,typename... T>
using variadic_type_template_index_t = variadic_type_template_index<index,T...>::type;
//Rest of the program
#include <stdlib.h> // For malloc
#include <iostream> // For std::cout
template <template <unsigned long long,unsigned long long> typename tocall,unsigned long long initval,unsigned long long times>
struct static_for;
template <template <unsigned long long,unsigned long long> typename tocall,unsigned long long initval,unsigned long long times>
requires requires(){tocall<initval,times>::toCall();}
struct static_for<tocall,initval,times>
{
static_assert(initval<times,"Invalid static_for loop");
constexpr static void call()
{
tocall<initval,times>::toCall();
static_for<tocall,initval +1,times>::call();
}
};
template <template <unsigned long long,unsigned long long> typename tocall,unsigned long long val>
struct static_for<tocall,val,val>
{
constexpr static void call()
{}
};
template <typename... Args>
struct loop_types
{
template <unsigned long long i,unsigned long long t>
struct loop
{
using type = variadic_type_template_index_t<i,Args...>;
static void toCall()
{
std::cout<<typeid(type).name()<<"\n";
}
};
};
template <typename... Args>
void call(Args...)
{
static_for<loop_types<Args...>::loop,0,sizeof...(Args)>::call();
}
int main()
{
call("1",2,true,malloc(5));
}
Error message I get
ToRun.cpp: In function 'void call(Args ...)':
ToRun.cpp:67:59: error: type/value mismatch at argument 1 in template parameter list for 'template<template<long long unsigned int <anonymous>, long long unsigned int <anonymous> > class tocall, long long unsigned int initval, long long unsigned int times> struct static_for'
67 | static_for<loop_types<Args...>::loop,0,sizeof...(Args)>::call();
| ^
ToRun.cpp:67:59: note: expected a class template, got 'loop_types<Args ...>::loop'
ToRun.cpp:67:66: warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
67 | static_for<loop_types<Args...>::loop,0,sizeof...(Args)>::call();
| ^~
ToRun.cpp:67:66: note: remove parentheses to default-initialize a variable
67 | static_for<loop_types<Args...>::loop,0,sizeof...(Args)>::call();
| ^~
| --
ToRun.cpp:67:66: note: or replace parentheses with braces to value-initialize a variable
How can I fix this problem?
Also, in case I replace Args... with a number of types ifself like with int,char,bool along with specifying number of types itself, the code works properly.
Compiler used - GCC
Command used
g++ ToRun.cpp -o ToRun -std=c++23 && "ToRun.exe"
I was expecting the program to print the types' name of every parameter I passes to call

For call() function... passing through a using should solve the problem
template <typename... Args>
void call(Args...)
{
using for_t = static_for<loop_types<Args...>::template loop, 0, sizeof...(Args)>;
for_t::call();
}
but remember to add a couple of typename in variadic_type_template_index specialization and using
template <unsigned long long index,typename T,typename... P>
struct variadic_type_template_index<index,T,P...>
{
using type = typename variadic_type_template_index<index-1,P...>::type;
// ..........^^^^^^^^
};
template <unsigned long long index,typename... T>
using variadic_type_template_index_t = typename variadic_type_template_index<index,T...>::type;
// ....................................^^^^^^^^
and also a dummy variadic_type_template_index<0> specialization, by example
template <>
struct variadic_type_template_index<0>
{
using type = void;
};
or you get a compilation error from
requires requires(){tocall<initval,times>::toCall();}
when initval is equal to times

Related

Why can't non-type template parameters be dependent on parameter of specialization?

I've got this code:
#include <iostream>
template <typename num_t, num_t num>
struct factorial {
consteval operator num_t() { return num * factorial<num_t, num - 1>{}; }
};
template <typename num_t>
struct factorial<num_t, 2> {
consteval operator char() { return 2; }
};
int main() {
std::cout << factorial<unsigned long long, 20>{} << '\n';
}
Clang gives me an error under 2 in struct factorial<num_t, 2>. The message is:
error: non-type template argument specializes a template parameter with dependent type 'num_t'
I've looked around a bit and it seems to be because of this part in the standard:
Paragraph [temp.class.spec] 14.5.5/8 of the standard:
The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization. [ Example:
template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error
template< int X, int (*array_ptr)[X] > class A {};
int array[5];
template< int X > class A<X,&array> { }; // error
—end example ]
Can someone explain the reasoning behind this section of the standard. It seems to me that they're simply getting in my way. What practical (or even theoretical) value comes with this rule?
Also, I'm more than a little confused about the wording that's being used. Can someone who understands maybe translate it into a more easy-to-understand phrasing?
This is a quick way to fix it:
template <typename num_t, num_t num, typename = void>
struct factorial {
constexpr operator num_t() { return num * factorial<num_t, num - 1>{}; }
};
template <typename num_t, num_t num>
struct factorial<num_t, num, std::enable_if_t<num == 2>> {
constexpr operator char() { return 2; }
};
The reason is (likely), you can't construct num_t num without being aware of what num_t is; thus, compiler cannot match the 2.
The paragraph basically says, template arguments are matched orthogonally, i.e., without co-dependency - this likely makes matching quicker in the compiler.

Is it possible to have a "generic" template parameter in C++, that can be either a non-type template parameter or a type?

In C++, it's possible to use a type as a template parameter, e.g:
template <typename T>
void MyFn();
It's also possible to use a non-type as a template parameter in some cases, e.g.:
template <int64_t T>
void MyFn2();
My question is whether it's possible to have a "generic" template parameter that can be both? Like:
template <TypenameOrint64_t T>
void MyFn3();
such that both MyFn3<42> and MyFn3<double> would be acceptable.
An example of how I might use this:
template <typename ValType, ValType Head, ValType ...Tail>
struct ListS{
template <typename OutType, template <ValType ArgType> class Fn>
using MapHead = ListS<OutType, Fn<Head>::val, Tail...>;
};
template<int64_t N>
struct SquareS{
static constexpr const int64_t val = N * N;
};
using Sqrd = ListS<int64_t, 3, 4>::MapHead<int64_t, SquareS>;
static_assert(std::is_same<Sqrd, ListS<int64_t, 9, 4>>::value, "Values don't match");
The above is a very rough sketch of a compile-time list of values along with a single compile-time "function" on it. Would it be possible to make something like that also support lists of types, not just lists of non-type template param compatible values, without just duplicating all the code?
Is it possible to have a “generic” template parameter in C++, that can be either a non-type template parameter or a type?
Short answer: no.
Long answer.
No. The best I can imagine to mix types and values is wrap values in types, using std::integral_constant, by example.
So, your desired code, could be written (C++17) almost as follows
#include <utility>
template <typename ...>
struct ListS;
template <typename ValType, ValType Head, ValType ...Tail>
struct ListS<std::integral_constant<ValType, Head>,
std::integral_constant<ValType, Tail>...>
{
template <template <auto> class Fn, typename OutType = ValType>
using MapHead = ListS<std::integral_constant<OutType, Fn<Head>::value>,
std::integral_constant<OutType, Tail>...>;
};
template <auto N>
struct SquareS : public std::integral_constant<decltype(N), N*N>
{ };
int main ()
{
using T1 = ListS<std::integral_constant<long long, 3ll>,
std::integral_constant<long long, 4ll>>;
using T2 = T1::MapHead<SquareS>;
using T3 = ListS<std::integral_constant<long long, 9ll>,
std::integral_constant<long long, 4ll>>;
static_assert( std::is_same_v<T2, T3> );
}
Pre C++17 you can't use auto for the type of the template values so you should make some simple corrections.
You could use function overloading and the auto type deduction that came with C++17
to accomplish something similar.
template<typename myType>
auto myFn3(myType value){
return value;
}
template<auto value> //takes any non-type parameter
auto myFn3(){
return value;
}
int main(){
auto test1_normal = myFn3(3);
auto test1_cast = myFn3<double>(3); //able to perform a cast
auto test1_auto = myFn3<3>();
return 0;
}

Using non-type template argument during partial specialization

Consider the following structs:
//Implementations provided elsewhere
struct A { A(int i, double d, std::string s); /* ... */ };
struct B { B(double d1, double d2); /* ... */ };
I have two conversion classes whose template signatures look like:
TupleAs< A, int, double, std::string > via1 { ... };
ArrayAs< B, double, 2 > via2 { ... };
Predictably, TupleAs converts a triplet of int,double, and std::string values into an object of type A. Similarly, ArrayAs converts a pair of two double values into an object of type B. (And yes, there are reasons why I cannot call the A and B constructors directly.)
Improving the syntax
I would like to change the syntax so I can do the following:
TupleAs< A(int,double,std::string) > via1 { ... };
ArrayAs< B(double,2) > via2 { ... };
which, I think, is more descriptive of a conversion process. The TupleAs template declaration and corresponding partial specialization would look like this:
template <typename T> struct TupleAs;
template <typename T, typename ... Args>
struct TupleAs<T(Args...)> { ... };
Compiler errors
However, if I try to do something similar with the ArrayAs version:
template <typename T> struct ArrayAs;
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
I get the following errors in clang (3.6) when trying to instantiate it (ArrayAs< B(double,2)> test;):
typeAs.cpp:14:22: error: unknown type name 'N'
struct ArrayAs<T(U,N)>{
^
typeAs.cpp:14:10: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
struct ArrayAs<T(U,N)>{
^~~~~~~~~~~~~~~
typeAs.cpp:13:45: note: non-deducible template parameter 'N'
template<typename T, typename U, unsigned N>
^
The gcc error diagnostic is a little different, but I won't post it here.
I admit that my templating skills should be better than they are, and I also concede that an analogous std::function<B(double,2)> declaration clearly is nonsense. But can someone tell me why the particular syntax I'm trying to achieve is not allowed? I looked through the C++14 standard and had trouble finding the relevant portion, and I'm having trouble interpreting the clang diagnostic message.
When you specialize TupleAs:
template <typename T, typename ... Args>
struct TupleAs<T(Args...)>
You are basically overloading the notation for a function. You are specializing on a function that takes Args... and returns a T. That is a type. You may not be using that function as a function, or really ever think about it as being a type, but that is what it is.
On the other hand, here:
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
There is no such thing as a function that takes N. It could take unsigned, but it can't take a value. There is just no such reasonable thing. From your example, B(double, 2) simply does not make sense. At best, you could write something that would allow:
template <unsigned N> using size_ = std::integral_constant<size_t, N>;
ArrayAs< B(double,size_<2>) >
Or even:
ArrayAs< B(std::array<double, 2>) >
since now we're back to using types everywhere. Whether you prefer that or not is personal preference.
The key here is that types are first-class citizens when it comes to all things template metaprogramming, and values should be avoided where possible.
template <typename T> struct ArrayAs;
template <typename T, typename U, std::size_t N>
struct ArrayAs<T(std::array<U,N>)> { ... };
works, as would:
template<class T>
struct to_array;
template<class T, size_t N>
struct to_array< T[N] > { using type = std::array<T, N>; };
template<class T>
using arr = typename to_array<T>::type;
then:
ArrayAs< Bob( arr<int[3]> ) > some_var;
live example.
Sadly, directly using ArrayAs< Bob( int[3] ) > doesn't work due to how arrays in function types decay to pointers.

Does C++ have a standard, compile-time type container?

(This question has an answer by Nim which mentions boost::mpl::map.)
Is there a compile-time container in standard C++ which can hold types?
A usage example would be:
compiler::vector foo{char, short, long, long long};
template <int N>
void bar(foo[N] param){/*do something and return foo[N]*/}
In c++11 you can use std::tuple : (disclaimer: not tested)
#include <tuple>
#include <type_traits>
std::tuple<char, short, long, long long> foo;
// reference type
template <int N>
void bar(decltype(std::get<N>(foo)) param){...}
// value type
template <int N>
void bar(std::remove_reference<decltype(std::get<N>(foo))>::type param)
Note that this is not completely what you want since you will either have only value or reference types, even if both are mixed in the tuple declaration of foo.
The value of the tuple are never used. I think with compiler optimization, foo will actually never be instanciated in the object code
As a type container, the standard provides you with std::tuple and -- as bogdan commented -- you can access the type elements using std::tuple_element.
using foo = std::tuple<char, short&, const long&&, long long>;
template <int N>
typename std::tuple_element<N,foo>::type bar(){/*...*/}
Even if std::tuple_element did not exist, you could easily build your own:
/// We want a metafunction to accept an index N into our type list LIST
template <unsigned N, typename LIST> struct
tuple_element;
/// Specialization for the case where N==0
template <template <typename ...> class LIST_T,typename T,typename...ELMS> struct
tuple_element<0,LIST_T<T,ELMS...>> {
using type = T; // just take the first type from the list
};
template <unsigned N, template <typename ...> class LIST_T,typename T,typename...ELMS> struct
tuple_element<N,LIST_T<T,ELMS...>> {
/// fallback for N>0: delegate the result type recursively until N->0
using type = typename tuple_element<N-1,LIST_T<ELMS...>>::type;
};
// create a convenience wrapper for the template
template <unsigned N, typename LIST> using
type_at = typename tuple_element<N, LIST>::type;
Now you can define your type list, e.g. like so:
using foo = std::tuple<char, short&, const long&&, long long>;
And you can easily access it's elements using type_at<N, foo>:
static_assert(std::is_same< type_at<0,foo>, char>::value,"error");
static_assert(std::is_same< type_at<1,foo>, short&>::value,"error");
static_assert(std::is_same< type_at<2,foo>, const long&&>::value,"error");
static_assert(std::is_same< type_at<3,foo>, long long>::value,"error");
template <int N>
type_at<N,foo> bar(){/*...*/}
This build's on Bérenger's answer, which put me onto the tuple concept. But I believe we can do better, even preserving references:
tuple foo<char&, short&, long&, long long&>;
template <int N>
void bar(tuple_element_t<N, decltype(foo)> param){}
In fact if there are no plans to use foo beyond this function we can even declare it inline:
template <int N>
void bar(tuple_element_t<N, tuple<char&, short&, long&, long long&>> param){}

C++ - Template function with volatile variable - can't call the template specialization

I'm trying to make a template function (in this case, in a class as a static method) and I can't seem to call the template specification I created. In other words, I expected the code to call the template specification I made and it doesn't.
class Atomic {
public:
template <typename T>
static T testAndSet(volatile T &t, T value)
{
ASSERT(false, "Unsupported type");
return T();
};
template <long>
static long testAndSet(volatile long &t, long value )
{
#if defined(_PC)
return InterlockedExchange(&t, value);
#else
return __sync_lock_test_and_set(&t, value);
#endif
};
};
Calling code:
volatile long counter = 0;
long newValue = 4;
Atomic::testAndSet( counter, newValue);
The callstack shows:
sample.exe!Atomic::testAndSet(volatile long & t=0, long value=4)
I also tried it this way but it didn't work.
template <typename T, typename TT>
static TT testAndSet(T &t, TT value) { ... }
template <volatile long, long>
static long testAndSet(volatile long &t, long value ) { ... }
or
template <typename T, typename TT>
static TT testAndSet(T t, TT value) { ... }
template <volatile long &, long>
static long testAndSet(volatile long &t, long value ) { ... }
Same thing... so now I have to try and understand what's happenning instead or brute forcing my way into it. Until then, I'll watch Lavavej's core c++ videos.. =)
You can explicitely specialize template functions, but that's not the standard method for functions. And you cannot do it like you do : what you've wrote is two template functions, one with a type parameter, the other with a long non-type parameter.
// This defines a template function parameted with a long
// vvvvvvvvvvvvvvv
template <long>
static long testAndSet(volatile long &t, long value )
You need to specify explicitely the template parameter on the call site for this overload to be considered, that's why it was never taken by the compiler.
You need overloading here, change the previous function with this one (explicit specialization is not needed):
// Not a template function
static long testAndSet(volatile long &t, long value)
Also, specializing a function template is done using the same syntax than for classes:
// Base case
template<typename T>
void function(T value);
// Explicit specialization (just an example)
template<>
void function<int>(int v, int a);
There are some differences between an explicit specialization and a simple overload, see this SO question for more details.
template <long>
static long testAndSet(volatile long &t, long value )
{
#if defined(_PC)
return InterlockedExchange(&t, value);
#else
return __sync_lock_test_and_set(&t, value);
#endif
};
That's not a specialization as you seem to think. It's an overload with a (unnamed) non-type template parameter. It'll never be selected during overload resolution for the call you made because you need to explicitly specify the parameter, like this for example: testAndSet<42>(counter, newValue);.
The syntax you want is:
template <> // <---
static long testAndSet<long>(volatile long &t, long value )
{ // ^^^^^^
//....
}
but at that point you could as well provide a normal overload.
You should always prefer overloading, because function template specializations can surprise you.