Is it possible to static_assert that a lambda is not generic? - c++

I implemented a Visit function (on a variant) that checks that the currently active type in the variant matches the function signature (more precisely the first argument). Based on this nice answer.
For example
#include <variant>
#include <string>
#include <iostream>
template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);
template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);
template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));
std::variant<int, std::string> data="abc";
template <typename V>
void Visit(V v){
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data)) {
std::cerr<< "alternative mismatch\n";
return;
}
v(std::get<Arg1>(data));
}
int main(){
Visit([](const int& i){std::cout << i << "\n"; });
Visit([](const std::string& s){std::cout << s << "\n"; });
// Visit([](auto& x){}); ugly kabooom
}
This works, but it explodes with a user unfriendly compile time error when users passes a generic (e.g. [](auto&){}) lambda. Is there a way to detect this and give nice static_assert() about it?
Would also be nice if it worked with function templates as well, not just with lambdas.
Note that I do not know what possible lambdas do, so I can not do some clever stuff with Dummy types since lambdas may invoke arbitrary functions on types.
In other words I can not try to call lambda in 2 std::void_t tests on int and std::string and if it works assume it is generic because they might try to call .BlaLol() on int and string.

Is there a way to detect this and give nice static_assert about it?
I suppose you can use SFINAE over operator() type.
Follows an example
#include <type_traits>
template <typename T>
constexpr auto foo (T const &)
-> decltype( &T::operator(), bool{} )
{ return true; }
constexpr bool foo (...)
{ return false; }
int main()
{
auto l1 = [](int){ return 0; };
auto l2 = [](auto){ return 0; };
static_assert( foo(l1), "!" );
static_assert( ! foo(l2), "!" );
}
Instead of a bool, you can return std::true_type (from foo() first version) or std::false_type (from second version) if you want to use it through decltype().
Would also be nice if it worked with function templates as well, not just with lambdas.
I don't think it's possible in a so simple way: a lambda (also a generic lambda) is an object; a template function isn't an object but a set of objects. You can pass an object to a function, not a set of objects.
But the preceding solution should works also for classes/structs with operator()s: when there is a single, non template, operator(), you should get 1 from foo(); otherwise (no operator(), more than one operator(), template operator()), foo() should return 0.

Yet another simpler option:
#include <type_traits>
...
template <typename V>
void Visit(V v) {
class Auto {};
static_assert(!std::is_invocable<V, Auto&>::value);
static_assert(!std::is_invocable<V, Auto*>::value);
...
}
The Auto class is just an invented type impossible to occur in the V parameters. If V accepts Auto as an argument it must be a generic.
I tested in coliru and I can confirm the solution covers these cases:
Visit([](auto x){}); // nice static assert
Visit([](auto *x){}); // nice static assert
Visit([](auto &x){}); // nice static assert
Visit([](auto &&x){}); // nice static assert
I'm not sure if that would cover all the possible lambdas that you don't know which are :)

#include <variant>
#include <string>
#include <iostream>
template <class U, typename T = void>
struct can_be_checked : public std::false_type {};
template <typename U>
struct can_be_checked<U, std::enable_if_t< std::is_function<U>::value > > : public std::true_type{};
template <typename U>
struct can_be_checked<U, std::void_t<decltype(&U::operator())>> : public std::true_type{};
template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);
template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);
template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));
std::variant<int, std::string> data="abc";
template <typename V>
void Visit(V v){
if constexpr ( can_be_checked<std::remove_pointer_t<decltype(v)>>::value )
{
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data))
{
std::cerr<< "alternative mismatch\n";
return;
}
v(std::get<Arg1>(data));
}
else
{
std::cout << "it's a template / auto lambda " << std::endl;
}
}
template <class T>
void foo(const T& t)
{
std::cout <<t << " foo \n";
}
void fooi(const int& t)
{
std::cout <<t << " fooi " << std::endl;
}
int main(){
Visit([](const int& i){std::cout << i << std::endl; });
Visit([](const std::string& s){std::cout << s << std::endl; });
Visit([](auto& x){std::cout <<x << std::endl;}); // it's a template / auto lambda*/
Visit(foo<int>);
Visit<decltype(fooi)>(fooi);
Visit(fooi);
// Visit(foo); // => fail ugly
}
I don't know if it's you want, but you can, with that static_assert if an auto lambda is passed as parameter.
I think it's not possible to do the same for template function, but not sure.

Related

Calling a lambda for each nth argument of multiple tuples?

I am trying to write a code that invokes a lambda with input arguments extracted from a variadic set of tuples. However, my attempt does not compile:
#include <iostream>
#include <tuple>
#include <utility>
#include <type_traits>
template <typename ...>
struct first_of;
template <typename T, typename ... Args>
struct first_of<T, Args...> {
using type = std::decay_t<T>;
};
template <typename T>
struct first_of<T> {
using type = std::decay_t<T>;
};
template <typename ... T>
using first_of_t = typename first_of<T...>::type;
template <typename Fn, typename... Tuples, std::size_t... Idxs>
void run_impl(Fn&& fn, std::index_sequence<Idxs...>, Tuples... t) {
auto temp = {(fn(std::get<Idxs>(t)...), true)...};
(void)temp;
}
template <typename Fn, typename... Tuples>
void run(Fn&& fn, Tuples&&... tuples) {
run_impl(std::forward<Fn>(fn), std::make_index_sequence<std::tuple_size<first_of_t<Tuples...>>::value>{}, std::forward<Tuples>(tuples)...);
}
int main() {
auto a = std::make_tuple(1, 2.34, "one");
auto b = std::make_tuple(32, 5.34, "two");
auto print = [](auto& f, auto& g) { std::cout << f << ", " << g << std::endl; };
run(print, a, b);
}
I am expecting the following output:
1, 32
2.34, 5.34
one, two
I am using c++14, so unfortunately, no fold expressions.
Here is the godbolt link to the code: https://godbolt.org/z/G19n5z
The easiest way to do this is to just add another layer of indirection, having run_impl delegate to another function that does the actual calling. I took the liberty of renaming your function to call_transposed():
template <std::size_t I, typename Fn, typename... Tuples>
void call_with_nth(Fn&& fn, Tuples&&... t) {
fn(std::get<I>(std::forward<Tuples>(t))...);
}
template <typename Fn, std::size_t... Idxs, typename... Tuples>
void call_transposed_impl(Fn&& fn, std::index_sequence<Idxs...>, Tuples&&... t) {
auto temp = {(call_with_nth<Idxs>(fn, std::forward<Tuples>(t)...), true)...};
(void)temp;
}
template <typename Fn, typename... Tuples>
void call_transposed(Fn&& fn, Tuples&&... tuples) {
call_transposed_impl(
std::forward<Fn>(fn),
std::make_index_sequence<std::tuple_size<first_of_t<Tuples...>>::value>{},
std::forward<Tuples>(tuples)...);
}
Godbolt link
I'm not certain why your code did not work, but I suspect that the std::get<Idxs>(t)... is trying to expand both packs Idxs and t at the same time, leaving you with no pack to expand later. This code avoids the problem by only dealing with a single pack at a time.

Enable template if functor passed as argument takes no argument

I am trying to define two overload for a template function.
First overload is generated if functor passed as an argument has no parameter otherwise second overload is generated.
I started to implement like this :
template <typename R, typename... Types>
constexpr size_t argumentCount(R (*f)(Types...))
{
return sizeof...(Types);
}
template <typename Function>
typename std::enable_if<argumentCount<Function>() == 0, int>::value = 0 > callFunc(Function fu)
{
fu();
}
template <typename Function, typename... Params>
typename std::enable_if<argumentCount<Function>() == 0, int>::value = 0 > callFunc(Function fu, Params... params)
{
fu(std::forward<decltype(params)>(params)...);
}
This does not compile for several reasons including parse error.What I want to do is,
callFunc([](){}); will call overload 1 while callFunc([](int value1,int value2){}); will call overload 2.
How can I achieve this ?
You can use the detection idiom with std::void_t and std::declval to detect such features.
template <typename FuncT>
using invocable_without_args_t = std::void_t<decltype(std::declval<FuncT>()())>;
More information is available here; this post helped me a lot to understand how this works.
If you can add a level of indirection... what about using tag-dispatching instead of SFINAE?
I mean, something as follows
#include <iostream>
#include <type_traits>
template <typename F>
void callFunc_helper (F fu, std::true_type)
{
std::cout << "zero version" << std::endl;
fu();
}
template <typename F, typename... Prs>
void callFunc_helper (F fu, std::false_type, Prs && ... params)
{
std::cout << "non zero version" << std::endl;
fu(std::forward<Prs>(params)...);
}
template <typename F, typename... Prs>
void callFunc (F fu, Prs && ... params)
{ callFunc_helper(fu, std::integral_constant<bool, 0u == sizeof...(Prs)>{},
std::forward<Prs>(params)...); }
int main ()
{
callFunc([]{});
callFunc([](int, int){}, 0, 1);
}
Obviously you can use std::integral_constant<bool, 0u == argumentCount<Function>()>{} if you really want to check the number of the functional arguments instead of the number of the following parameters (but why?).
Here is your version with a fixed syntax:
template <typename R, typename... Types>
constexpr size_t argumentCount(R (*f)(Types...))
{
return sizeof...(Types);
}
template <typename Function>
typename std::enable_if<argumentCount<Function>() == 0, int> callFunc(Function fu)
{
fu();
}
template <typename Function, typename... Params>
typename std::enable_if<argumentCount<Function>() == 0, int> callFunc(Function fu, Params... params)
{
fu(std::forward<decltype(params)>(params)...);
}
In this particular example you do not need an extra overload, Types... can be empty.
template <typename Function, typename... Params>
auto callFunc(Function fu, Params... params)
{
fu(std::forward<decltype(params)>(params)...);
}
works perfectly well.
If you need two template functions, just write them:
#include <iostream>
template <class R>
void callFunc(R (*f)()) {
std::cout << "Called no-arg template\n";
f();
}
template <class R, class T, class... Types, class... Params>
void callFunc(R (*f)(T, Types...), Params... params) {
std::cout << "Called multi-arg template\n";
f(params...);
}
void g() {
std::cout << "Called g\n";
}
void h(int) {
std::cout << "Called h\n";
}
int main() {
callFunc(g);
callFunc(h, 3);
return 0;
}
Output:
[temp]$ clang++ -std=c++11 test.cpp
[temp]$ ./a.out
Called no-arg template
Called g
Called multi-arg template
Called h
[temp]$

Is there an elegant solution for selecting between callable and non-callable type in a Variadic Template in C++14

I'm implementing simple text formatter for a class. The main function in it could receive a list of values that will be concatenated. Or optionally, for cases where the parameters are not friends of ostream, I accept a conversion function as first parameter that will convert all other parameters into a std::string.
The following code shows the idea, but it does not compile. For simplicity, I will output to cout in the example.
struct formater{
template<typename P, typename... PS>
void format(const P& p, const PS&... ps){
if (std::is_convertible<P, std::function<void()>>::value){
cout << p(ps...) << endl;
} else {
cout << p;
log(ps...);
}
}
};
The reason the code does not compile is, if P is callable, it will not be possible to output it to cout in the "else" branch, and if P is not callable, it will tell P is not callable and cannot receive ps... parameters in the "then" branch.
I thought to use enable_if, but since I'm defining both cases (T and F) of the condition, I get redefinition of same function and also fail to compile.
I could try to mimic static_if, but it doesn't look elegant at all.
I'm wondering if there is some elegant way to check that P is callable and SFINAE it. Maybe exploiting that I know the parameter types of P, (PS...)->std::string.
There are two problems. First, just like you said, an if is a runtime branch only, and second, you are not checking for an object to be callable, but you are checking if an object is callable with no arguments. Any callable object that must take arguments won't pass your test.
I would first implement that trait. Note that this is not necessary for C++17:
template<typename, typename = void>
struct is_callable : std::false_type {};
template<typename F, typename... Args>
struct is_callable<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))>> : std::true_type {};
Then, you can use std::enable_if:
struct Formatter {
template<typename F, typename... Args>
auto format(F func, Args&&... args) -> std::enable_if_t<is_callable_v<F(Args...)>> {
std::cout << func(std::forward<Args>(args)...);
std::cout << std::endl;
}
template<typename T>
auto format(T&& value) -> std::enable_if_t<!is_callable_v<T()>> {
std::cout << std::forward<T>(value);
std::cout << std::endl;
}
};
You can implement void_t like this:
template<typename...>
using void_t = void;
You can go check this live example: Live at coliru
Please note that in C++17, you have the constexpr if and std::is_invocable:
struct Formatter {
template<typename F, typename... Args
void format(F&& param, Args&&... args) {
if constexpr (std::is_invocable_v<F, Args...>) {
std::cout << std::invoke(std::forward<F>(param), std::forward<Args>(args)...);
} else {
std::cout << std::forward<F>(param);
log(std::forward<Args>(args)...);
}
}
};
You could use std::enable_if:
struct formater{
template<typename P, typename... PS>
std::enable_if<std::is_convertible<PARAM, std::function<void(PS...)>>::value, void>::type
format(const PARAM& p, const PS&... ps){
cout << p(ps...) << endl;
}
template<typename P, typename... PS>
std::enable_if<!std::is_convertible<PARAM, std::function<void(PS...)>>::value, void>::type
format(const PARAM& p, const PS&... ps){
cout << p;
log(ps...);
}
};

Handling a void variable in a templatized function in C++11

I have a template class that must perform some operation before calling a function whose parameters and return type are generic.
This is the method:
template <typename ReturnType, typename ...Args>
ReturnType function (Args ...args) {
// prepare for call
// ...
ReturnType rv = makeCall(args...); // [1]
// dismiss the call
// ...
return rv;
}
Of course it's compiling correctly when ReturnType is not void.
When I use it in this context:
function<void>(firstArg, secondArg);
The compiler responds with
error: return-statement with a value, in function returning 'void' [-fpermissive]
pointing to the line marked with [1].
Is there any solution other than passing -fpermissive to the compiler?
I would prefer to have a unique method, because I possible solution I found is to instantiate different versions using enable_if and is_same.
Thank you in advance.
-- Update --
This is a complete example. I should have said that our functions are indeed class methods.
#include <type_traits>
#include <iostream>
class Caller {
public:
Caller() {}
template <typename ReturnType, typename ...Arguments>
ReturnType call(Arguments ... args) {
prepare();
ReturnType rv = callImpl<ReturnType>(args...);
done();
return rv;
}
private:
void prepare() {
std::cout << "Prepare\n";
}
void done() {
std::cout << "Done\n";
}
template <typename ReturnType, typename ...Arguments>
typename std::enable_if<std::is_same<ReturnType, void>::value, ReturnType>::type callImpl ( Arguments ... args) {
std::cout << "Calling with void\n";
return;
}
template <typename ReturnType, typename ...Arguments>
typename std::enable_if<std::is_same<ReturnType, bool>::value, ReturnType>::type callImpl (Arguments ... args) {
std::cout << "Calling with bool\n";
return true;
}
template <typename ReturnType, typename ...Arguments>
typename std::enable_if<std::is_same<ReturnType, int>::value, ReturnType>::type callImpl (Arguments ... args) {
std::cout << "Calling with int\n";
return 42;
}
};
int main(int argc, char *argv[]) {
Caller c;
auto rbool = c.call<bool> (1,20);
std::cout << "Return: " << rbool << "\n";
auto rint = c.call<int> (1,20);
std::cout << "Return: " << rint << "\n";
// the next line fails compilation. compile with --std=c++11
c.call<void>("abababa");
return 0;
}
-- Update --
Not a big issue: Use std::bind(&Caller::callImpl<ReturnType>, this, args).
Here's my attempt at a general C++11-compliant solution that you can easily reuse.
Let's start by creating a simple type trait that converts void to an empty struct. This doesn't introduce any code repetition.
struct nothing { };
template <typename T>
struct void_to_nothing
{
using type = T;
};
template <>
struct void_to_nothing<void>
{
using type = nothing;
};
template <typename T>
using void_to_nothing_t = typename void_to_nothing<T>::type;
We also need a way to call an arbitrary function converting an eventual void return type to nothing:
template <typename TReturn>
struct helper
{
template <typename TF, typename... Ts>
TReturn operator()(TF&& f, Ts&&... xs) const
{
return std::forward<TF>(f)(std::forward<Ts>(xs)...);
}
};
template <>
struct helper<void>
{
template <typename TF, typename... Ts>
nothing operator()(TF&& f, Ts&&... xs) const
{
std::forward<TF>(f)(std::forward<Ts>(xs)...);
return nothing{};
}
};
template <typename TF, typename... Ts>
auto with_void_to_nothing(TF&& f, Ts&&... xs)
-> void_to_nothing_t<
decltype(std::forward<TF>(f)(std::forward<Ts>(xs)...))>
{
using return_type =
decltype(std::forward<TF>(f)(std::forward<Ts>(xs)...));
return helper<return_type>{}(std::forward<TF>(f), std::forward<Ts>(xs)...);
}
Usage:
template <typename ReturnType, typename ...Args>
void_to_nothing_t<ReturnType> function (Args ...args) {
// prepare for call
// ...
auto rv = with_void_to_nothing(makeCall, args...); // [1]
// dismiss the call
// ...
return rv;
}
live wandbox example
There's a proposal by Matt Calabrese called "Regular Void" that would solve this issue. You can find it here: "P0146R1".
Depending on what you wish to accomplish in the lines
// dismiss the call
you might be able to use:
template <typename ReturnType, typename ...Args>
ReturnType function (Args ...args) {
// prepare for call
// ...
CallDismisser c;
return makeCall(args...); // [1]
}
That would work as long as the destructor of CallDismisser can do everything you need to do.
struct nothing {};
template<class Sig>
using returns_void = std::is_same< std::result_of_t<Sig>, void >;
template<class Sig>
using enable_void_wrap = std::enable_if_t< returns_void<Sig>{}, nothing >;
template<class Sig>
using disable_void_wrap = std::enable_if_t< !returns_void<Sig>{}, std::result_of_t<Sig> >;
template<class F>
auto wrapped_invoker( F&& f ) {
return overload(
[&](auto&&...args)->enable_void_wrap<F(decltype(args)...)> {
std::forward<F>(f)(decltype(args)(args)...);
return {};
},
[&](auto&&...args)->disable_void_wrap<F(decltype(args)...)> {
return std::forward<F>(f)(decltype(args)(args)...);
}
);
}
so wrapped_invoker takes a function object, and makes it return nothing instead of void.
Next, holder:
template<class T>
struct holder {
T t;
T&& get()&& { return std::forward<T>(t); }
};
template<>
struct holder<void> {
template<class T>
holder(T&&) {} // discard
void get()&& {}
};
holder lets you hold the return value and convert back to void if needed. You must create holder<T> using {} to get reference lifetime extension to work properly. Adding a ctor to holder<T> will break it.
holder<void> silently discards anything passed to it.
template <typename ReturnType, typename ...Args>
ReturnType function (Args ...args) {
// prepare for call
// ...
holder<ReturnType> rv{ wrapped_invoker(makeCall)(args...) };
// dismiss the call
// ...
return std::move(rv).get();
}
Now, holder<ReturnType> holds either nothing or the return value of makeCall(args...).
If it holds nothing, rv.get() returns void, and it is legal to return void to a function where ReturnValue is void.
Basically we are doing two tricks. First, we are preventing makeCall from returning void, and second if we are returning void we are discarding the return value of makeCall conditionally.
overload isn't written here, but it is a function that takes 1 or more function objects (such as lambdas) and returns their overload set. There is a proposal for std::overload, and a myriad of examples on stackoverflow itself.
Here is some:
Overloaded lambdas in C++ and differences between clang and gcc
C++11 “overloaded lambda” with variadic template and variable capture
The problem seems to be with //Dismiss the call.
This code shouldn't exist. That's what we have RAII for. The following code does work, even with ReturnType = void.
template <typename ReturnType, typename ...Arguments>
ReturnType call(Arguments ... args) {
Context cx;
return callImpl<ReturnType>(args...);
}
Context::Context() { std::cout << "prepare\n"; }
Context::~Context() { std::cout << "done\n"; }

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;
}