How to access first parameter in parameter pack? - c++

Is it possible to statically "unroll" a parameter list at compile time, giving using one parameter in every "unroll" step? I think variadic templates are the way to go combined with partial template specialization, but I cannot get this example to run:
#include <iostream>
char static const text1[] = "Foo";
char static const text2[] = "FooBar";
template <char const * TEXT, unsigned int N, char const *... REST, unsigned int... Ns>
void doStuff() {
std :: cout << TEXT << "-" << N << std :: endl;
doStuff<REST..., Ns...>();
}
template <char const * TEXT, unsigned int N>
void doStuff() {
std :: cout << TEXT << std :: endl;
}
void doStuff() {}
int main() {
doStuff<text1,3,text2,5>();
return 0;
}
My expected output would be Foo-3\nFooBar-5
However, clang++ 3.8 gives me:
error: no matching function for call to 'doStuff'
doStuff<text1,3,text2,5>();
^~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:7:6: note: candidate template ignored: invalid explicitly-specified argument for template
parameter 'REST'
void doStuff() {
^
test.cpp:13:6: note: candidate template ignored: invalid explicitly-specified argument for template
parameter 'N'
void doStuff() {
^

In C++17, you might do something like
template <char const * TEXT, unsigned int N>
void doStuff() {
std::cout << TEXT << "-" << N << std::endl;
}
template <auto v1, auto v2, auto ... values>
void doStuff()
{
std :: cout << v1 << "-" << v2 << std :: endl;
doStuff<values...>();
}
Currently you have to pack your values by pair:
template<const char* S, int N>
struct pairValue {
static constexpr const char* s = S;
static constexpr int n = N;
};
template <typename ... Ts>
void doStuff()
{
const int dummy[] = {0, ((std::cout << Ts::s << "-" << Ts::n << std::endl), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable.
}
And call it:
doStuff<pairValue<text1, 3>, pairValue<text2, 5>>();

You can use them as parameters to work around it:
#include <iostream>
#include<type_traits>
char static const text1[] = "Foo";
char static const text2[] = "FooBar";
constexpr void doStuff() {}
template <typename T, typename U, typename... O>
constexpr
std::enable_if_t<
std::is_same<T, const char *>::value
and std::is_same<U, int>::value
> doStuff(T str, U num, O... o) {
std :: cout << str << "-" << num << std :: endl;
doStuff(o...);
}
int main() {
doStuff(text1,3,text2,5);
return 0;
}

Related

ambiguous template specialization in variadic function template

I am trying to write a variadic function template to calculate the byte size of a struct. This is going to be used in a network programming project I am working on. The first step I came up with this without the variadic template that works:
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
// if I change the definition of Position, I need to remember to change this function
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I compiled with g++ 9.3.0 on my Ubuntu 20.04 laptop and it worked fine. However if I try to make it a variadic template then I ran into different compilation errors. The point of this variadic template is to be able to write like the following, so that the implementation doesn't depend on the explicit knowledge of x and y's types.
Here is bad code #1
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I got
error: ambiguous template specialization 'sizeT' for 'std::size_t sizeT()
note: candidate are: 'template std::size_t sizeT()'
note: template<class T, class... Ts> std::size_t sizeT()'
Bad code #2. If I put the variadic template at a different location:
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
template<>
inline std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I got
error: call of overloaded 'sizeT<int8_t>()' is ambiguous
note candidate: 'std::size_t sizeT() [with T=signed char; std::size_t = long unsigned int]'
note candidate: 'std::size_t sizeT() [with T=signed char; Ts={}; std::size_t = long unsigned int]'
error: call of overloaded 'sizeT()' is ambiguous
note candidate: 'std::size_t sizeT() [with T=Position; std::size_t = long unsigned int]'
note candidate: 'std::size_t sizeT() [with T=Position; Ts={}; std::size_t = long unsigned int]'
I kinda understand case #2 but I don't understand #1. How can I achieve this? Thank you so much in advance.
You don't use variadic...
You might do (C++17)
template <typename ... Ts>
constexpr std::size_t sizeT() { return (0 + ... + sizeof(Ts)); }
template <>
constexpr std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
Demo
But sizeT<Position, Position> would return 2 * sizeof(Position) instead of 2 * sizeT<Position> (which are identical here).
You might do instead:
template <typename... Ts>
struct tag{};
template <typename T>
constexpr std::size_t sizeT(tag<T>) { return sizeof(T); }
template <typename ... Ts>
constexpr std::size_t sizeT(tag<Ts...>) { return (0 + ... + sizeT(tag<Ts>())); }
template <typename ... Ts>
constexpr std::size_t sizeT(tag<Ts>...) { return (0 + ... + sizeT(tag<Ts>())); }
constexpr std::size_t sizeT(tag<Position>)
{
return sizeT(tag<decltype(Position::x), decltype(Position::y)>());
}
Demo

Two variadic template arguments with the same size

I wanna implement a Print function which works this:
Print<1, 3>("Hello", "World");
and I hope that it will print "Hello" one time and "World" 3 times.I wonder how to implement it.
Below is my stupid code, of course it failed when compiling:
template <unsigned int n, unsigned int ...n_next,
typename T, typename ...Ts>
void Print(T & t, Ts & ... ts)
{
for(int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
Print<n_next..., ts...>(ts...);
}
template <unsigned int n, typename T>
void Print(T & t)
{
for(int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
}
This will make it:
template <unsigned int n, typename T>
void Print(T&& t)
{
for(int i = 0; i < n; i++)
{
std::cout << std::forward<T>(t) << " ";
}
std::cout << std::endl;
}
template <std::size_t Idx1, std::size_t... Idx, class T, class... Ts>
void Print(T&& t, Ts&& ... ts) {
Print<Idx1>(std::forward<T>(t));
using expand = int[];
(void)expand{(Print<Idx>(std::forward<Ts>(ts)), 0) ...};
}
I also propose a completely different solution that avoid at all recursion and for() loops.
It simulate template folding in C++14 in initialization of an unused C-style array.
First the main Print(), that expand the variadic lists calling a Print_h() helper function, passing to it the values and list (index sequence) correspinding to numbers of iteration for every value
template <std::size_t ... Ns, typename ... Ts>
void Print (Ts ... ts)
{
using unused=int[];
(void)unused { 0, (Print_h(std::make_index_sequence<Ns>{}, ts), 0)... };
}
Next the helper function that uses the same trick for multiple printing
template <std::size_t ... Is, typename T>
void Print_h (std::index_sequence<Is...>, T const & t)
{
using unused=std::size_t[];
(void)unused { 0, (std::cout << t << " ", Is)... };
std::cout << std::endl;
}
The following is the full compiling C++14 example
#include <utility>
#include <iostream>
template <std::size_t ... Is, typename T>
void Print_h (std::index_sequence<Is...>, T const & t)
{
using unused=std::size_t[];
(void)unused { 0, (std::cout << t << " ", Is)... };
std::cout << std::endl;
}
template <std::size_t ... Ns, typename ... Ts>
void Print (Ts ... ts)
{
using unused=int[];
(void)unused { 0, (Print_h(std::make_index_sequence<Ns>{}, ts), 0)... };
}
int main ()
{
Print<1u, 3u>("hello", "world");
}
If you can't use C++14 but only C++11, isn't difficult to develop substitutes for std::make_index_sequence and std::index_sequence (both available only from C++14).
Obviously in C++17 you can use template folding simplifying the functions as follows
template <std::size_t ... Is, typename T>
void Print_h (std::index_sequence<Is...>, T const & t)
{
((std::cout << t << " ", (void)Is), ...);
std::cout << std::endl;
}
template <std::size_t ... Ns, typename ... Ts>
void Print (Ts ... ts)
{ (Print_h(std::make_index_sequence<Ns>{}, ts), ...); }
I see four problems in your code
(1) the recursive call
Print<n_next..., ts...>(ts...);
is wrong because you have to use Ts... types in template argument list, not ts... values
Print<n_next..., Ts...>(ts...);
or, better (because permit a trick I explain next) without explicating the types
Print<n_next...>(ts...);
(2) is better if you receive as const references the values
template <unsigned int n, unsigned int ...n_next,
typename T, typename ...Ts>
void Print(T const & t, Ts ... ts)
// ..........^^^^^
otherwise you can't call Print() with constant values as follows
Print<1u, 3u>(1, "world");
(3) better use unsigned value for indexes in for loops because you have to test they with unsigned values (minor problem)
// ..VVVVVVVV
for (unsigned int i = 0; i < n; i++)
(4) you have to place the ground case for Print() (the one that receive only a value) before the recursive case.
I suggest to substitute they with
template <typename = void>
void Print ()
{ }
because, this way, all prints are done in recursive version an you don't need to repeat equal code in two different function (but you have to call Print<n_next...>(ts...); the recursion.
So I propose to modify your code as follows
#include <iostream>
template <typename = void>
void Print ()
{ }
template <unsigned int n, unsigned int ...n_next,
typename T, typename ...Ts>
void Print(T const & t, Ts ... ts)
{
for(auto i = 0u; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
Print<n_next...>(ts...);
}
int main ()
{
Print<1u, 3u>("hello", "world");
}
You can make your code work by just swapping the declarations of the two overloads and removing the ts... template argument in the recursive call:
template <unsigned int n, typename T>
void Print(T & t)
{
for(unsigned int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
}
template <unsigned int n, unsigned int ...n_next,
typename T, typename ...Ts>
void Print(T & t, Ts & ... ts)
{
for(unsigned int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
Print<n_next...>(ts...);
}
(also, be consistant with signedness)
Demo
Furthermore, you don't need to duplicate the printing part, just call the other overload:
template <unsigned int n, typename T>
void Print(T & t)
{
for(unsigned int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
}
template <unsigned int n, unsigned int ...n_next,
typename T, typename ...Ts>
void Print(T & t, Ts & ... ts)
{
Print<n>(t);
Print<n_next...>(ts...);
}
Alternatively, if you can use C++17 for fold expressions, you can do the following (use forwarding references and std::forward if you need to):
template<typename T>
void Print(unsigned int n, T& t)
{
for(unsigned int i = 0; i < n; i++)
{
std::cout << t << " ";
}
std::cout << std::endl;
}
template<unsigned int... Ns, typename... Ts>
void Print(Ts&... ts)
{
(Print(Ns, ts), ...);
}
Demo

unpacking tuple for method argument

I have a syntax problem with expanding a tuple to its content.
The working code I have:
class Example
{
public:
static void Go( int i, float f)
{
std::cout << "p1: " << i << std::endl;
std::cout << "p2: " << f << std::endl;
}
template <typename T, size_t ... I>
static void Do( T parm )
{
Go( std::get<I>( parm)...);
}
};
int main()
{
using X = std::tuple<int, float>;
Example::Do<X,0,1>( std::make_tuple( 1,2.2)) ;
}
But I want to call the expansion with something like
int main()
{
using X = std::tuple<int, float>;
using IDX = std::std::index_sequence_for<int, float>;
Example::Do<X,IDX>( std::make_tuple( 1,2.2)) ;
}
So I am searching for something like ( which can not compiled... ):
template <typename T, size_t ... I>
static void Do<T, std::index_sequence<I...>>(T parm)
{
Go( std::get<I>( parm)...);
}
Pass the index sequence by value:
template <typename T, size_t... I>
static void Do(T parm, std::index_sequence<I...>)
{
Go(std::get<I>(parm)...);
}
Call the method like this:
Example::Do(std::make_tuple(1, 2.2),
std::index_sequence_for<int, float>{});
The problem is that your std::index_sequence (IDX) is not expanding in the template parameters to what you need:
Example::Do<X, IDX>(std::make_tuple(1, 2.2));
...will not "expand" to:
Example::Do<X, 0, 1>(std::make_tuple(1, 2.2)); // This works
What you need is to let the compiler deduce the template arguments for ...I, to do so change your static method to:
template <typename T, size_t ... I>
static void Do(T parm, std::index_sequence<I...>)
{
Go(std::get<I>(parm)...);
}
And then call it with:
Example::Do(std::make_tuple(1, 2.2), IDX{});
The compiler will automatically deduce the template arguments and call Example::Do<X, 0, 1> as needed.
If you want to be able to call Do without the second arguments, you can add another layer of abstraction in Example:
class Example
{
public:
static void Go(int i, float f) {
std::cout << "p1: " << i << std::endl;
std::cout << "p2: " << f << std::endl;
}
template <typename Tuple>
static void Do(Tuple &&parm) {
_Do(std::forward<Tuple>(parm),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>{}>{});
}
private:
template <typename Tuple, size_t... I>
static void _Do(Tuple &&parm, std::index_sequence<I...>) {
Go(std::get<I>(std::forward<Tuple>(parm))...);
}
};
Then:
Example::Do(std::make_tuple(1, 2.2));
Note that the three versions:
Example::Do<X, 0, 1> (std::make_tuple(1, 2.2));
Example::Do(std::make_tuple(1, 2.2), IDX{});
Example::Do(std::make_tuple(1, 2.2));
Will likely results in the same code after compiler optimization (on my machine with clang++-3.7 and -O1, the three assembly files are strictly identical).

difference between array pointer to array reference

I want to write a function which distinguish between arrays and pointers. This is needed in order to figure size of literal strings. I tried:
template<typename Ty>
void f(const Ty* rhs) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
template<typename Ty, size_t Dm>
void f(const Ty(&rhs)[Dm]) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
int main(int, char*[]) {
const char arr0[] = "test2";
const char* ptr = "test3";
const char arr6[6] = "test4";
f("test1");
f(arr0);
f(ptr);
f(arr6);
return 0;
}
But the compiler (VS2013) tells me that the call is ambiguous. Any hints?
Thanks in advance.
Unfortunately, the call are ambiguous.
As workaround, you may add an extra layer:
template<typename Ty>
void f_pointer(const Ty* rhs) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
template<typename Ty, size_t Dm>
void f_array(const Ty(&rhs)[Dm]) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
template<typename T>
std::enable_if_t<std::is_array<T>::value>
f(const T&t)
{
f_array(t);
}
template<typename T>
std::enable_if_t<!std::is_array<T>::value>
f(const T&t)
{
f_pointer(t);
}
Live Demo
An alternative to Jarod42's answer (which works) is to use class template specialization:
#include <iostream>
#include <type_traits>
template <typename T, bool is_array>
struct f_helper {
static void print_type (T& arg) {
std::cout << arg << " is an array\n";
}
};
template <typename T>
struct f_helper<T, false> {
static void print_type (T arg) {
std::cout << arg << " is not an array\n";
}
};
template <typename T>
void f (T& arg) {
f_helper<T, std::is_array<T>::value>::print_type (arg);
}
int main(int, char*[]) {
const char arr0[] = "test2";
const char* ptr = "test3";
const char arr6[6] = "test4";
f("test1");
f(arr0);
f(ptr);
f(arr6);
return 0;
}
Live demo
Change the reference to a pointer in the second overload of the function template:
template<typename T, size_t S> void f(T(&)[S]){
cout << "array with size " << S << "\n";
}
template <typename T> void f(T*&) {
cout << "pointer \n";
}
Live Demo
I don't believe it's possible to do what you're trying the way you're trying. The following statements evaluate to the same thing:
int* var
int var[]
It's all a matter of syntax sugar. Moreover, adding a size to the array on the function header has only the benefit of warning the user of the expected array size. Therefore the two are also equivalent (as far as the compiler is concerned):
void f(int* v)
void f(int v[8])

Template partial specialization

template<class T>
struct TypeX;
template<>
struct TypeX<int(...)>//HERE IF WITHOUT ELLIPSIS IT WILL COMPILE
{
static std::string get_type()
{
return "int()";
}
};
template<>
struct TypeX<int>
{
static std::string get_type()
{
return "int";
}
};
template<class T>
struct type_descriptor
{
typedef T type;
typedef typename std::remove_reference<T>::type no_ref_type;
typedef typename std::remove_pointer<no_ref_type>::type no_ref_no_pointer_type;
typedef typename std::remove_cv<no_ref_no_pointer_type>::type no_ref_no_pointer_no_cv_type;
typedef typename std::remove_all_extents<no_ref_no_pointer_no_cv_type>::type no_ref_no_pointer_no_cv_no_ext_type;
typedef no_ref_no_pointer_no_cv_no_ext_type bare_type;
enum {isArray = std::is_array<T>::value, isPointer = std::is_pointer<T>::value, isRef = std::is_reference<T>::value};
static std::string get_type()
{
return pointer_<isPointer>() + array_<std::is_array<no_ref_no_pointer_type>::value>() + TypeX<bare_type>::get_type();
}
};
template<bool C>
std::string array_()
{return "";}
template<>
std::string array_<true>()
{return "array of";}
template<bool C>
std::string pointer_()
{return "";}
template<>
std::string pointer_<true>()
{return "pointer to";}
int _tmain(int argc, _TCHAR* argv[])
{
cout << type_descriptor<int(*)()>::get_type();
return 0;
}
Please see comment in a code. The question is why if I specialize for ellipsis, which suppose to mean any number I'm getting an error, but when I specialize for no arguments it compiles?
The question is why if I specialize
for ellipsis, which suppose to mean
any number I'm getting an error, but
when I specialize for no arguments it
compiles?
Because ellipsis doesn't imply any number of parenthesis (as you're trying to use it in main) . ellipsis is used to imply variable number of arguments in function (C++03).
EDIT: Maybe the following example gives you enough hint to implement what you want to:
template<class T>
struct TypeX
{
TypeX() { cout << "TypeX" << endl; }
};
template<typename T>
struct TypeX<T(*)()> //will match with : int (*)(), char (*)(), etc!
{
TypeX() { cout << "TypeX<T(*)()>" << endl; }
};
template<typename T, typename S>
struct TypeX<T(*)(S)> //will match with : int (*)(int), char (*)(int), etc!
{
TypeX() { cout << "TypeX<T(*)(S)>" << endl; }
};
template<typename T, typename S, typename U>
struct TypeX<T(*)(S, U)> //will match with : int (*)(int, char), char (*)(int, int), etc!
{
TypeX() { cout << "TypeX<T(*)(S, U)>" << endl; }
};
int main() {
TypeX<int*>();
TypeX<int(*)()>();
TypeX<int(*)(int)>();
TypeX<int(*)(char)>();
TypeX<int(*)(char, int)>();
TypeX<int(*)(short, char)>();
return 0;
}
Output:
TypeX
TypeX<T(*)()>
TypeX<T(*)(S)>
TypeX<T(*)(S)>
TypeX<T(*)(S, U)>
TypeX<T(*)(S, U)>
Demo at ideone : http://www.ideone.com/fKxKK
An elipsis means the function can accept any number of arguments at each place of invocation.
int f(...); // signature
int x = f(); // invocations
int y = f(my_int, my_double);
The function signature itself is distinct from the more specific signatures naively implied by each invocation (i.e. int f(), int f(int, double)).
Therefore, while you can specialise for the int (*)(...) case, only a function type that actually specifies an initial elipsis will match. Other functions like int (*)(int) do not match.