CUDA kernel "Only a single pack parameter is allowed" workaround? - c++

The CUDA 7 standard regarding variadic global function templates states "only a single pack parameter is allowed." Is there an elegant workaround for this? I want to be able to do something like:
template<int... vals>
void RecursiveFunct() {
}
template<int... vals, typename T, typename... Args>
void RecursiveFunct(T t, Args... args) {
t.template call<vals...>();
RecursiveFunct<vals...>(args...);
}
I'm thinking I could wrap my pack of integers into something before passing them along but is it possible to do that in a way that is transparent to the caller of this code?

Not sure to understand your exactly limits but... I suppose that std::integer_sequence and a wrapper function to call call() can help you.
The following is a toy, but compilable, example that show what I mean.
struct foo
{
template <int ... vals>
void call () const
{ std::cout << "- call " << sizeof...(vals) << std::endl; }
};
template <typename IS>
void RecursiveFunct (IS const &)
{ }
template <typename T, int ... vals>
void wrapCall (T const & t, std::integer_sequence<int, vals...> const &)
{ t.template call<vals...>(); }
template<typename IS, typename T, typename ... Args>
void RecursiveFunct (IS const & is, T t, Args... args)
{
wrapCall(t, is);
RecursiveFunct(is, args...);
}
int main ()
{
// print 5 times "- call 4"
RecursiveFunct(std::integer_sequence<int, 2, 3, 5, 7>{},
foo{}, foo{}, foo{}, foo{}, foo{});
}
Take in count that std::integer_sequence is a C++14 feature, so the preceding code needs (at least) a C++14 compiler.
But if you need to work with C++11, it's trivial to create a std::integer_sequence substitute.
By example
template <typename T, T ... ts>
struct myIntegerSequence
{ };
-- EDIT --
The OP ask
can this work without creating an instance of integer_sequence?
In normal C++14, yes. Works this with Cuda? I don't know.
I've obtained this changing the wrapCall() func with a wrapCall struct and a func() static method. This because I've used the partial specialization that can't be used with funcs.
The folling is the toy example
#include <utility>
#include <iostream>
struct foo
{
template <int ... vals>
void call () const
{ std::cout << "- call " << sizeof...(vals) << std::endl; }
};
template <typename>
void RecursiveFunct ()
{ }
template <typename>
struct wrapCall;
template <int ... vals>
struct wrapCall<std::integer_sequence<int, vals...>>
{
template <typename T>
static constexpr void func (T const & t)
{ t.template call<vals...>(); }
};
template<typename IS, typename T, typename ... Args>
void RecursiveFunct (T t, Args... args)
{
wrapCall<IS>::func(t);
RecursiveFunct<IS>(args...);
}
int main ()
{
// print 5 times "- call 4"
RecursiveFunct<std::integer_sequence<int, 2, 3, 5, 7>>
(foo{}, foo{}, foo{}, foo{}, foo{});
}
But are you sure that is a problem the istantiation of a std::integer_sequence?

Related

Variadic template method specialization

This is the code I currently have:
class Foo
{
public:
template<typename T, typename... Args>
void Function(T t1, Args... args){
// Definition
}
private:
template<typename T>
void Function(T t1){
// Definition
}
};
#include "header.h"
int main()
{
Foo foo;
foo.Function(1, 2, 3, 4, 5);
return 0;
}
Works just fine. When I try to separate the definition to source.cpp, the gcc starts complaining. I know I have to specialize the templates in order to separate the definition, so I tried adding the code below to the header file:
template<>
void Foo::Function<int, int...>(int t1, int... args);
template<>
void Foo::Function<int>(int);
but without success. What am I missing
edit: gcc error messages:
header.h:15:28: error: expansion pattern ‘int’ contains no argument
packs void Foo::Function(int t1, int... args);
header.h:15:48: error: expansion pattern ‘int’ contains no argument
packs void Foo::Function(int t1, int... args);
You can't use int... as a parameter pack, and so this doesn't work. In addition, to separate the source from the definition, you have to fully specify the template, so int... wouldn't work even if that syntax were allowed.
Ways to get around this.
1. Make Function accept an initializer list.
We can write function so that it accepts an initializer list of ints:
#include <initializer_list>
class Foo {
public:
void Function(int t1, std::initializer_list<int> t2);
};
void Foo::Function(int t1, std::initializer_list<int> t2) {
for(int i : t2) {
// stuff
}
}
Now, you can call Function pretty easily, and it's not even templated:
Foo f;
f.Function(10, {1, 2, 3, 4, 5});
If there are other places you're using templates, you can expand a parameter pack directly into the initializer list:
template<class... Args>
void invoke_foo(Foo& f, int first, Args... rest) {
f.Function(first, {rest...});
}
2. Use SFINAE to disable all non-int overloads. We can disable all overloads of Foo::Function that don't only accept ints
#include <type_traits>
class Foo {
public:
// Requires C++17 for std::conjunction
// You could write your own std::conjunction too if you'd prefer
template<class... Args>
auto Function(int t1, Args... t2)
-> std::enable_if_t<std::conjunction<std::is_same<Args, int>...>::value>
{
// stuff
}
};
The downside to this is that non-integral values won't automatically be converted to int.
There is better way to do it.
First of all it looks like you want to force same type of all arguments (this is done by std::initializer_list in accepted answer). This can be foreced by providing extra explicit argument:
class Foo
{
public:
template<typename T, typename... Args>
void Function(T t1, T t2, Args... args)
{
LOG;
this->Function(t1);
this->Function(t2, args...);
}
private:
template<typename T>
void Function(T t1)
{
LOG << VAR(t1);
}
};
template<>
void Foo::Function<int>(int x)
{
LOG << " Spec" << VAR(x);
}
As you can see it is enough if you provide specialization of method for single argument.
Live demo

Optimizing while using std::enable_if

Consider this code:
#include <iostream>
#include <type_traits>
template <std::size_t N> void bar() { std::cout << "bar<" << N << ">() called.\n"; }
template <std::size_t N> void hit() { std::cout << "hit<" << N << ">() called.\n"; }
template <typename T> struct evaluate : std::bool_constant<std::is_integral_v<T>> {
static constexpr std::size_t size = sizeof(T); // Simplified for illustration only.
};
void foo() { }
template <typename T, typename... Args>
std::enable_if_t<!evaluate<T>::value> foo (const T&, const Args&...);
template <typename T, typename... Args>
std::enable_if_t<evaluate<T>::value> foo (const T&, const Args&... args) {
bar<evaluate<T>::size>();
// Do whatever.
foo(args...);
}
template <typename T, typename... Args>
std::enable_if_t<!evaluate<T>::value> foo (const T&, const Args&... args) {
hit<evaluate<T>::size>();
// Do whatever, but different from the previous foo overload.
foo(args...);
}
int main() {
foo (5, "hello", true);
}
Output:
bar<4>() called.
hit<6>() called.
bar<1>() called.
How to rewrite the above so that evaluate<T> needs only be computed once instead of twice with each foo iteration?
You maybe like this one:
template <std::size_t N> void bar() { std::cout << "bar<" << N << ">() called.\n"; }
template <std::size_t N> void hit() { std::cout << "hit<" << N << ">() called.\n"; }
template <typename T>
struct evaluate : std::bool_constant<std::is_integral_v<T>>
{
static constexpr std::size_t size = sizeof(T); // Simplified for illustration only.
};
void foo() { }
template <typename T, typename... Args>
void foo( const T&, const Args&... args)
{
using X = evaluate<T>;
if constexpr ( X::value )
{
bar<X::size>();
}
else
{
hit<X::size>();
}
foo( args... );
}
int main() {
foo (5, "hello", true);
}
It "calls" only once evaluate<T>, which is not important but maybe easier to read. That all the template code is only used during instantiation makes it only a matter of taste.
As you mention c++17 you can use constexpr if to get rid of SFINAE at all in your example. This makes it also possible to reuse common lines of code in both variants of foo which is quite nice. The executable will not be much different you can believe, but the maintainability is much better I think!
Ok, I thought evaluate was computed twice. But how would you make it appear only once (without using macros)? We are supposed to avoid repetition in code anyways
You can try to save it as a additional template parameter with default value
Something as
template <typename T, typename... Args, typename E = evaluate<T>>
std::enable_if_t<!E::value> foo (const T&, const Args&...);
template <typename T, typename... Args, typename E = evaluate<T>>
std::enable_if_t<E::value> foo (const T&, const Args&... args)
{
bar<E::size>();
// Do whatever.
foo(args...);
}
template <typename T, typename ... Args, typename E>
std::enable_if_t<!E::value> foo (const T&, const Args&... args)
{
hit<E::size>();
// Do whatever, but different from the previous foo overload.
foo(args...);
}

Variadic Function

I am trying to write a class to represent a tensor Tensor and would like to provide the syntax tensor(i, j) for a 2 dimensional tensor, tensor (i, j, k) for a 3 dimensional tensor and so on.
What I want to know is if there is a c++ type safe way to declare such Tensor:operator()(int, int, ...) that accepts any number of int arguments (besides the C style with the macros va_start va_end) and how to use said arguments inside the function.
Thanks for your time.
Well, you could always use an ordinary parameter pack; but force a compilation failure unless all parameters are ints:
#include <utility>
#include <type_traits>
class foo {
public:
template<typename ...Args,
typename=std::void_t<std::enable_if_t
<std::is_same_v<Args, int>>...>>
void operator()(Args ...args)
{
}
};
void bar()
{
foo bar;
bar(4, 2);
}
That will compile, but not this:
bar(4, "foo");
or
bar(4, 2.3);
Note that this will not compile either:
unsigned baz=2;
bar(4, baz);
If you need to accept unsigned values, tweat the template accordingly.
Note that the template does not need to use a forwarding reference, since the only acceptable parameters are plain ints. Within the template function, you now have a garden-variety parameter pack, that you would use the same way you'd use any other parameter pack.
To accept also unsigned int and other types that are convertible to int, and if you can accept an upper limit (63, in the following example) to the number of integer argument, I propose to follow an example from W.F..
So you can develop a typer
template <typename T, std::size_t>
using typer = T;
and a recursive struct proOp
template <typename T, std::size_t N = 64U,
typename = std::make_index_sequence<N>>
struct proOp;
template <typename T, std::size_t N, std::size_t... Is>
struct proOp<T, N, std::index_sequence<Is...>> : public proOp<T, N-1U>
{
using proOp<T, N-1U>::operator();
void operator() (typer<T, Is>... ts)
{ }
};
template <typename T>
struct proOp<T, 0U, std::index_sequence<>>
{
void operator() ()
{ }
};
Inheriting from proOp<int>, Tensor become
struct Tensor : public proOp<int>
{
using proOp<int>::operator();
};
The following is a full working example
#include <utility>
template <typename T, std::size_t>
using typer = T;
template <typename T, std::size_t N = 64U,
typename = std::make_index_sequence<N>>
struct proOp;
template <typename T, std::size_t N, std::size_t... Is>
struct proOp<T, N, std::index_sequence<Is...>> : public proOp<T, N-1U>
{
using proOp<T, N-1U>::operator();
void operator() (typer<T, Is>... ts)
{ }
};
template <typename T>
struct proOp<T, 0U, std::index_sequence<>>
{
void operator() ()
{ }
};
struct Tensor : public proOp<int>
{
using proOp<int>::operator();
};
int main()
{
Tensor t;
t(1, 2, 3);
t(1, 2, 3, 4U); // accept also unsigned
//t(1, "two"); // error
}
Another way could be make operator() recursive and use the first argument in every recursion
// recursive case
template <typename ... Ts>
void operator() (int i0, Ts ... is)
{
// do something with i0
this->operator()(is...); // recursion
}
void operator() ()
{ }
The following is a full working example
struct Tensor
{
// recursive case
template <typename ... Ts>
void operator() (int i0, Ts ... is)
{
// do something with i0
this->operator()(is...); // recursion
}
void operator() ()
{ }
};
int main()
{
Tensor t;
t(1, 2, 3);
t(1, 2, 3, 4U); // accept also unsigned
//t(1, "two"); // error
}
There is a more laconic way for creating safe variadic function, without using recursion and std::enable_if:
template <typename ...Ints>
void function(int first, Ints... other)
{
int values[] = {first, other...};
for(int value : values)
{
//your code
}
}
This way is also type safe and function(1, "2", 3) won't compile.

How to store variadic template arguments?

Is it possible to store a parameter pack somehow for a later use?
template <typename... T>
class Action {
private:
std::function<void(T...)> f;
T... args; // <--- something like this
public:
Action(std::function<void(T...)> f, T... args) : f(f), args(args) {}
void act(){
f(args); // <--- such that this will be possible
}
}
Then later on:
void main(){
Action<int,int> add([](int x, int y){std::cout << (x+y);}, 3, 4);
//...
add.act();
}
To accomplish what you want done here, you'll have to store your template arguments in a tuple:
std::tuple<Ts...> args;
Furthermore, you'll have to change up your constructor a bit. In particular, initializing args with an std::make_tuple and also allowing universal references in your parameter list:
template <typename F, typename... Args>
Action(F&& func, Args&&... args)
: f(std::forward<F>(func)),
args(std::forward<Args>(args)...)
{}
Moreover, you would have to set up a sequence generator much like this:
namespace helper
{
template <int... Is>
struct index {};
template <int N, int... Is>
struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};
template <int... Is>
struct gen_seq<0, Is...> : index<Is...> {};
}
And you can implement your method in terms of one taking such a generator:
template <typename... Args, int... Is>
void func(std::tuple<Args...>& tup, helper::index<Is...>)
{
f(std::get<Is>(tup)...);
}
template <typename... Args>
void func(std::tuple<Args...>& tup)
{
func(tup, helper::gen_seq<sizeof...(Args)>{});
}
void act()
{
func(args);
}
And that it! So now your class should look like this:
template <typename... Ts>
class Action
{
private:
std::function<void (Ts...)> f;
std::tuple<Ts...> args;
public:
template <typename F, typename... Args>
Action(F&& func, Args&&... args)
: f(std::forward<F>(func)),
args(std::forward<Args>(args)...)
{}
template <typename... Args, int... Is>
void func(std::tuple<Args...>& tup, helper::index<Is...>)
{
f(std::get<Is>(tup)...);
}
template <typename... Args>
void func(std::tuple<Args...>& tup)
{
func(tup, helper::gen_seq<sizeof...(Args)>{});
}
void act()
{
func(args);
}
};
Here is your full program on Coliru.
Update: Here is a helper method by which specification of the template arguments aren't necessary:
template <typename F, typename... Args>
Action<Args...> make_action(F&& f, Args&&... args)
{
return Action<Args...>(std::forward<F>(f), std::forward<Args>(args)...);
}
int main()
{
auto add = make_action([] (int a, int b) { std::cout << a + b; }, 2, 3);
add.act();
}
And again, here is another demo.
You can use std::bind(f,args...) for this. It will generate a movable and possibly copyable object that stores a copy of the function object and of each of the arguments for later use:
#include <iostream>
#include <utility>
#include <functional>
template <typename... T>
class Action {
public:
using bind_type = decltype(std::bind(std::declval<std::function<void(T...)>>(),std::declval<T>()...));
template <typename... ConstrT>
Action(std::function<void(T...)> f, ConstrT&&... args)
: bind_(f,std::forward<ConstrT>(args)...)
{ }
void act()
{ bind_(); }
private:
bind_type bind_;
};
int main()
{
Action<int,int> add([](int x, int y)
{ std::cout << (x+y) << std::endl; },
3, 4);
add.act();
return 0;
}
Notice that std::bind is a function and you need to store, as data member, the result of calling it. The data type of that result is not easy to predict (the Standard does not even specify it precisely), so I use a combination of decltype and std::declval to compute that data type at compile time. See the definition of Action::bind_type above.
Also notice how I used universal references in the templated constructor. This ensures that you can pass arguments that do not match the class template parameters T... exactly (e.g. you can use rvalue references to some of the T and you will get them forwarded as-is to the bind call.)
Final note: If you want to store arguments as references (so that the function you pass can modify, rather than merely use, them), you need to use std::ref to wrap them in reference objects. Merely passing a T & will create a copy of the value, not a reference.
Operational code on Coliru
This question was from C++11 days. But for those finding it in search results now, some updates:
A std::tuple member is still the straightforward way to store arguments generally. (A std::bind solution similar to #jogojapan's will also work if you just want to call a specific function, but not if you want to access the arguments in other ways, or pass the arguments to more than one function, etc.)
In C++14 and later, std::make_index_sequence<N> or std::index_sequence_for<Pack...> can replace the helper::gen_seq<N> tool seen in 0x499602D2's solution:
#include <utility>
template <typename... Ts>
class Action
{
// ...
template <typename... Args, std::size_t... Is>
void func(std::tuple<Args...>& tup, std::index_sequence<Is...>)
{
f(std::get<Is>(tup)...);
}
template <typename... Args>
void func(std::tuple<Args...>& tup)
{
func(tup, std::index_sequence_for<Args...>{});
}
// ...
};
In C++17 and later, std::apply can be used to take care of unpacking the tuple:
template <typename... Ts>
class Action
{
// ...
void act() {
std::apply(f, args);
}
};
Here's a full C++17 program showing the simplified implementation. I also updated make_action to avoid reference types in the tuple, which was always bad for rvalue arguments and fairly risky for lvalue arguments.
I think you have an XY problem. Why go to all the trouble to store the parameter pack when you could just use a lambda at the callsite? i.e.,
#include <functional>
#include <iostream>
typedef std::function<void()> Action;
void callback(int n, const char* s) {
std::cout << s << ": " << n << '\n';
}
int main() {
Action a{[]{callback(13, "foo");}};
a();
}

How to reverse the order of arguments of a variadic template function?

I have a template function with varargs template arguments, like this
template<typename Args...>
void ascendingPrint(Args... args) { /* ... */ }
And I want to write
template<typename Args...>
void descendingPrint(Args... args) {
/* implementation using ascendingPrint()? */
}
How do I reverse the order of the parameter-pack args before passing it along, i.e. in pseudo-code:
template<typename Args...>
void descendingPrint(Args... args) {
ascendingPrint( reverse(args) );
}
Overall approach and usage
The overal approach consists in packing the arguments into an std::tuple of references, exploiting the perfect forwarding machinery of std::forward_as_tuple().
This means that, at run-time, you should incur in very small overhead and no unnecessary copy/move operations. Also, the framework does not use recursion (apart from compile-time recursion, which is unavoidable for generating indices), so no risk of run-time overhead even in case the compiler would not manage to inline the recursive function calls (which is unlikely anyway, so this is more of an academic argument).
Moreover, this solution is general, in that you can use it as a header-only library to invoke your functions with reversed arguments and with minimum effort: descending_print() should be just a minimal thin wrapper around ascending_print().
Here is how it should look like:
MAKE_REVERT_CALLABLE(ascending_print)
template<typename... Args>
void descending_print(Args&&... args)
{
revert_call(REVERT_ADAPTER(ascending_print), std::forward<Args>(args)...);
}
What follows is a presentation of the implementation.
First step: reverting a type sequence
Here is a simple way to revert a type sequence:
#include <tuple>
#include <type_traits>
template<typename, typename>
struct append_to_type_seq { };
template<typename T, typename... Ts>
struct append_to_type_seq<T, std::tuple<Ts...>>
{
using type = std::tuple<Ts..., T>;
};
template<typename... Ts>
struct revert_type_seq
{
using type = std::tuple<>;
};
template<typename T, typename... Ts>
struct revert_type_seq<T, Ts...>
{
using type = typename append_to_type_seq<
T,
typename revert_type_seq<Ts...>::type
>::type;
};
A small test program:
int main()
{
static_assert(
std::is_same<
revert_type_seq<char, int, bool>::type,
std::tuple<bool, int, char>
>::value,
"Error"
);
}
And a live example.
Second step: reverting a tuple
The next step consists in reverting a tuple. Given the usual indices trick machinery:
template <int... Is>
struct index_list { };
namespace detail
{
template <int MIN, int N, int... Is>
struct range_builder;
template <int MIN, int... Is>
struct range_builder<MIN, MIN, Is...>
{
typedef index_list<Is...> type;
};
template <int MIN, int N, int... Is>
struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
{ };
}
template<int MIN, int MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;
Together with the functions defined above, a tuple can easily be reverted this way:
template<typename... Args, int... Is>
typename revert_type_seq<Args...>::type
revert_tuple(std::tuple<Args...> t, index_list<Is...>)
{
using reverted_tuple = typename revert_type_seq<Args...>::type;
// Forwarding machinery that handles both lvalues and rvalues...
auto rt = std::forward_as_tuple(
std::forward<
typename std::conditional<
std::is_lvalue_reference<
typename std::tuple_element<Is, reverted_tuple>::type
>::value,
typename std::tuple_element<Is, reverted_tuple>::type,
typename std::remove_reference<
typename std::tuple_element<Is, reverted_tuple>::type
>::type
>::type
>(std::get<sizeof...(Args) - Is - 1>(t))...
);
return rt;
}
template<typename... Args>
typename revert_type_seq<Args...>::type
revert_tuple(std::tuple<Args...> t)
{
return revert_tuple(t, index_range<0, sizeof...(Args)>());
}
Here is a simple test program:
#include <iostream>
int main()
{
std::tuple<int, int, char> t(42, 1729, 'c');
auto rt = revert_tuple(t);
std::cout << std::get<0>(rt) << " "; // Prints c
std::cout << std::get<1>(rt) << " "; // Prints 1729
std::cout << std::get<2>(rt) << " "; // Prints 42
}
Here is a live example.
Third step: reverting a function's arguments
The final step consists in unpacking the tuple when calling our target function. Here is another generic utility to save us a couple of lines:
template<typename... Args>
typename revert_type_seq<Args...>::type
make_revert(Args&&... args)
{
auto t = std::forward_as_tuple(std::forward<Args>(args)...);
return revert_tuple(t);
}
The above function creates a tuple whose elements are the arguments provided, but in reverse order. We are not ready to define our target:
template<typename T>
void ascending_print(T&& t)
{
std::cout << std::forward<T>(t) << " ";
}
template<typename T, typename... Args>
void ascending_print(T&& t, Args&&... args)
{
ascending_print(std::forward<T>(t));
ascending_print(std::forward<Args>(args)...);
}
The above function(s) prints all the arguments provided. And here is how we could write descending_print():
template<typename T, int... Is>
void call_ascending_print(T&& t, index_list<Is...>)
{
ascending_print(std::get<Is>(std::forward<T>(t))...);
}
template<typename... Args>
void descending_print(Args&&... args) {
call_ascending_print(make_revert(std::forward<Args>(args)...),
index_range<0, sizeof...(Args)>());
}
A simple test case again:
int main()
{
ascending_print(42, 3.14, "Hello, World!");
std::cout << std::endl;
descending_print(42, 3.14, "Hello, World!");
}
And of course a live example.
Final step: simplification
The above solution may be non-trivial to understand, but it can be made trivial to use, and quite flexible. Given a couple of generic functions:
template<typename F, typename... Args, int... Is>
void revert_call(F&& f, index_list<Is...>, Args&&... args)
{
auto rt = make_revert(std::forward<Args>(args)...);
f(std::get<Is>(rt)...);
}
template<typename F, typename... Args>
void revert_call(F&& f, Args&&... args)
{
revert_call(f, index_range<0, sizeof...(Args)>(),
std::forward<Args>(args)...);
}
And a couple of macro definitions (I couldn't find a way to create an overload set for a function template, sorry):
#define MAKE_REVERT_CALLABLE(func) \
struct revert_caller_ ## func \
{ \
template<typename... Args> void operator () (Args&&... args) \
{ func(std::forward<Args>(args)...); } \
};
#define REVERT_ADAPTER(func) \
revert_caller_ ## func()
It becomes really easy to adapt any function for being called with arguments in reverse order:
MAKE_REVERT_CALLABLE(ascending_print)
template<typename... Args>
void descending_print(Args&&... args)
{
revert_call(REVERT_ADAPTER(ascending_print), std::forward<Args>(args)...);
}
int main()
{
ascending_print(42, 3.14, "Hello, World!");
std::cout << std::endl;
descending_print(42, 3.14, "Hello, World!");
}
To conclude, as usual, a live example.
I think instead of reversing the arguments, you can reverse your logic! For example reverse the operations on arguments.
template <typename T>
void ascendingPrint(const T& x)
{
cout << x << " ";
}
template<typename T, typename ... Args>
void ascendingPrint(const T& t, Args... args)
{
ascendingPrint(t); // First print `t`
ascendingPrint(args...); // Then print others `args...`
}
template <typename T>
void descendingPrint(const T& x)
{
cout << x << " ";
}
template<typename T, typename ... Args>
void descendingPrint(const T& t, Args... args)
{
descendingPrint(args...); // First print others `args...`
descendingPrint(t); // Then print `t`
}
and then
int main()
{
ascendingPrint(1, 2, 3, 4);
cout << endl;
descendingPrint(1, 2, 3, 4);
}
Output
1 2 3 4
4 3 2 1
Here's the simple approach I mentioned in the comments: Generating indices in reverse and unpacking a tuple with that.
// reversed indices...
template<unsigned... Is> struct seq{ using type = seq; };
template<unsigned I, unsigned... Is>
struct rgen_seq : rgen_seq<I-1, Is..., I-1>{};
template<unsigned... Is>
struct rgen_seq<0, Is...> : seq<Is...>{};
#include <tuple>
namespace aux{
template<class Tup, unsigned... Is>
void descending_print(Tup&& t, seq<Is...>)
{
ascending_print(std::get<Is>(std::forward<Tup>(t))...);
}
} // aux::
template<class... Args>
void descending_print(Args&&... args)
{
auto t = std::forward_as_tuple(std::forward<Args>(args)...);
aux::descending_print(t, rgen_seq<sizeof...(Args)>{});
}
Live example.
Here is a recursive implementation of a specialized revert<>:
// forward decl
template<class ...Tn>
struct revert;
// recursion anchor
template<>
struct revert<>
{
template<class ...Un>
static void apply(Un const&... un)
{
ascendingPrint(un...);
}
};
// recursion
template<class T, class ...Tn>
struct revert<T, Tn...>
{
template<class ...Un>
static void apply(T const& t, Tn const&... tn, Un const&... un)
{
// bubble 1st parameter backwards
revert<Tn...>::apply(tn..., t, un...);
}
};
// using recursive function
template<class A, class ...An>
void descendingPrint(A const& a, An const&... an)
{
revert<An...>::apply(an..., a);
}
It works with gcc-4.6/7/8 and clang and is probably standard compliant -- the only difficult part being the call of revert<Tn...>::apply(tn..., t, un...).
It has drawbacks though (as recursion often has), that it generates a lot of template-instantiations of the target function (code bloat) and does not use perfect forwarding, which may be an issue (but maybe could be improved to use it).
My solution supports perfect forwarding and does not involve a recursion:
#include <iostream>
#include <utility>
#include <tuple>
#include <cstdlib>
template< typename ...types >
void
ascendingPrint(types &&... _values)
{
(std::cout << ... << std::forward< types >(_values)) << std::endl;
}
template< typename ...types, std::size_t ...indices >
void
descendingPrintHelper(std::tuple< types... > const & refs, std::index_sequence< indices... >)
{
constexpr std::size_t back_index = sizeof...(indices) - 1;
return ascendingPrint(std::forward< std::tuple_element_t< back_index - indices, std::tuple< types... > > >(std::get< back_index - indices >(refs))...);
}
template< typename ...types >
void
descendingPrint(types &&... _values)
{
auto const refs = std::forward_as_tuple(std::forward< types >(_values)...);
return descendingPrintHelper(refs, std::make_index_sequence< sizeof...(types) >{});
}
int
main()
{
ascendingPrint(1, ' ', 2, ' ', 3);
descendingPrint(1, ' ', 2, ' ', 3);
return EXIT_SUCCESS;
}
Live example (or even simplier).
Also modern compilers can perfectly optimize out all the unnecessary stuff: https://godbolt.org/g/01Qf6w
This can be done using C++17 fold expression and a little trick for right-to-left order execution.
#include <iostream>
template< typename T> void print(T&& val) { std::cout << val; }
template< typename ... Types > void descendingPrint(Types&&... vals) {
int tmps = 0;
((print(vals), tmps) = ...);
}
int main() {
descendingPrint(1, ' ', 2, ' ', 3);
return 0;
}