I am trying to create a variadic template function which would call a function to consecutive pairs of arguments.
The desired function signature would be:
template <typename ...Ts>
void apply(Ts &...args);
When called with apply(t1, t2, t3) the function should make a sequence of calls func(t1, t2)and func(t2, t3), where func is a function with signature:
template <typename L, typename R>
void func(L &left, R &right);
The order of operations is not really relevant in my context. The function has to be able to modify objects left and right, hence passed by reference. I cannot simply use polymorphic access through a base class pointer, since the objects have different class templates, a shared class cannot really be taken out.
Is it possible to achieve such a sequence of calls via a variadic template function? None of the pack expansion and fold expression examples that I've seen seem to cover such a scenario. Or should I pass my objects in a different fashion?
My initial attempt, included below (with some details omitted), packed all template parameters into a tuple, and then used a ‘const for-loop’ to ‘loop’ through the tuple elements. However, I soon came to realize that this approach would not work, because the lambda in the const-for loop invokes operator() const and therefore cannot modify the passed objects.
The code I was using does make the desired sequence of calls, but the objects are not modified (set_something() is not a const function). I had to resort to using a wrapper function with different numbers of template parameters, and making the calls to func manually.
template <std::size_t Begin, typename Callable, std::size_t... I>
constexpr void const_for_impl(Callable &&func, std::index_sequence<I...>) {
(func(std::integral_constant<std::size_t, Begin + I>{}), ...);
}
template <std::size_t Begin, std::size_t End, typename Callable>
constexpr void const_for(Callable &&func) {
const_for_impl<Begin>(std::forward<Callable>(func),
std::make_index_sequence<End - Begin>{});
};
template <typename... Ts>
void apply(Ts *... args) {
auto tuple = std::make_tuple(std::forward<Ts>(args)...);
const_for<0, sizeof...(args) - 1>(
[&](auto I) { func((std::get<I>(tuple)), (std::get<I + 1>(tuple))); });
};
template <typename L, typename R>
void func(L &l, R &r) {
// Validate with some type traits
static_assert(has_some_property<L>::value);
static_assert(has_another_property<R>::value);
// Get a shared pointer to something common
auto common = std::make_shared<typename something_common<L, R>::type>();
l.set_something(common);
r.set_something(common);
};
// Application scenario
int main() {
ComplexObjectA<SomeType, SomeParameter> a;
ComplexObjectB<AnotherType, AnotherParameter> b;
ComplexObjectC c;
apply(a, b, c);
return 0;
}
So, what's the problem? Simple fold-like template (and remember that template pattern-matching goes in reverse!)
template<typename T1, typename T2>
void apply(T1 &&t1, T2 &&t2) { func(t1, t2); }
template<typename T1, typename T2, typename... Ts>
void apply(T1 &&t1, T2 &&t2, Ts &&...ts) {
func(t1, t2);
return apply(t2, ts...);
}
Or, more precise, it should actually look as (thanks #MaxLanghof):
void apply(T1 &&t1, T2 &&t2) {
func(std::forward<T1>(t1), std::forward<T2>(t2));
}
template<typename T1, typename T2, typename... Ts>
void apply(T1 &&t1, T2 &&t2, Ts &&...ts) {
func(std::forward<T1>(t1), t2);
return apply(std::forward<T2>(t2), std::forward<TS>(ts)...);
}
An alternative (c++14) approach:
#include <utility>
#include <utility>
#include <tuple>
#include <iostream>
template <typename L, typename R>
void func(L &left, R &right) {
std::cout << left << " " << right << std::endl;
}
template <typename Tup, std::size_t... Is>
void apply_impl(Tup&& tup, std::index_sequence<Is...>) {
int dummy[] = { 0, (static_cast<void>(func(std::get<Is>(tup), std::get<Is + 1>(tup))), 0)... };
static_cast<void>(dummy);
}
template <typename ...Ts>
void apply(Ts &...args) {
apply_impl(std::forward_as_tuple(args...), std::make_index_sequence<sizeof...(Ts) - 1>{});
}
int main() {
int arr[] = {0, 1, 2, 3};
apply(arr[0], arr[1], arr[2], arr[3]);
}
Output:
0 1
1 2
2 3
[online example]
To make it c++11 compliant one would need to use one of the available integer_sequence implementations.
Related
Is it possible to interleave template parameters inside of function call sites?
I effectively want to do implement the following, but I don't know how (psuedo code):
template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
static_assert(sizeof...(indices) == sizeof...(Ts));
constexpr n = sizeof...(Ts);
bar(
indices[0], parse<Ts[0]>(things[0]),
indices[1], parse<Ts[1]>(things[1]),
...
indices[n-1], parse<Ts[n-1]>(things[n-1]));
}
Note: I know the following can be done (psuedo code):
template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
static_assert(sizeof...(indices) == sizeof...(Ts));
constexpr n = sizeof...(Ts);
bar(
indices[0], indices[1], ..., indices[n-1],
parse<Ts[0]>(things[0]),
parse<Ts[1]>(things[1]),
...
parse<Ts[n-1]>(things[n-1]));
}
A partial solution I came up with is to add a swizzling component:
template <typename Func>
decltype(auto) swizzle()
{
return Func();
}
template <typename Func, typename T0>
decltype(auto) swizzle(size_t i0, T0 &&t0)
{
return Func(i0, std::forward<T0>(t0));
}
template <typename Func, typename T0, typename T1>
decltype(auto) swizzle(size_t i0, size_t i1, T0 &&t0, T1 &&t1)
{
return Func(i0, std::forward<T0>(t0), i1, std::forward<T1>(t1));
}
but I think I have to manually write each case per arity I want to consider.
Like this:
template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
std::apply([](auto...args) {
bar(args...);
}, std::tuple_cat(std::make_tuple(indices, parse<Ts>(*(things++)))...));
}
If bar is a lambda instead of a function template, you can just pass bar directly as the first argument to std::apply.
If you want to avoid copying the return values of parse<Ts>(*(things++)), you can use std::forward_as_tuple instead of std::make_tuple.
One slightly more verbose alternative if the *(things++) makes you uncomfortable is to use std::index_sequence:
template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
[=]<auto... Is>(std::index_sequence<Is...>) {
std::apply([](auto...args) {
bar(args...);
}, std::tuple_cat(std::make_tuple(indices, parse<Ts>(things[Is]))...));
}(std::index_sequence_for<Ts...>());
}
In some contexts, it could be useful/necessary to have a for loop evaluated/unrolled at compile time. For example, to iterate over the elements of a tuple, one needs to use std::get<I>, which depends on a template int parameter I, hence it has to be evaluated at compile time.
Using compile recursion one can solve a specific problem, as for instance discussed here, here, and, specifically for std::tuple here.
I am interested, however, on how to implement a generic compile-time for loop.
The following c++17 code implements this idea
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <int start, int end, template <int> class OperatorType, typename... Args>
void compile_time_for(Args... args)
{
if constexpr (start < end)
{
OperatorType<start>()(std::forward<Args>(args)...);
compile_time_for<start + 1, end, OperatorType>(std::forward<Args>(args)...);
}
}
template <int I>
struct print_tuple_i {
template <typename... U>
void operator()(const std::tuple<U...>& x) { std::cout << std::get<I>(x) << " "; }
};
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3, print_tuple_i>(x);
return 0;
}
While the code works, it would be nicer to be able to simply provide a template function to the routine compile_time_for, rather than a template class to be instantiated at each iteration.
A code like the following, however, does not compile in c++17
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <int start, int end, template <int, typename...> class F, typename... Args>
void compile_time_for(F f, Args... args)
{
if constexpr (start < end)
{
f<start>(std::forward<Args>(args)...);
compile_time_for<start + 1, end>(f, std::forward<Args>(args)...);
}
}
template <int I, typename... U>
void myprint(const std::tuple<U...>& x) { std::cout << std::get<I>(x) << " "; }
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3>(myprint, x);
return 0;
}
With gcc 7.3.0 and option std=c++17 the first error is
for2.cpp:7:25: error: ‘auto’ parameter not permitted in this context
void compile_time_for(F f, Args... args)
The questions are:
Is there a way to write compile_time_for such that it accepts a template function as its first argument?
If question 1. is positive, is there an overhead in the first working code, due to the fact that the routine create an object of type OperatorType<start> at every loop iteration?
Are there plans to introduce a feature like a compile-time for loop in the upcoming c++20?
Is there a way to write compile_time_for such that it accepts a template function as its first argument?
Short answer: no.
Long answer: a template function isn't an object, is a collection of objects and you can pass to a function, as an argument, an object, non a collection of objects.
The usual solution to this type of problem is wrap the template function inside a class and pass an object of the class (or simply the type, if the function is wrapped as a static method). That is exactly the solution you have adopted in your working code.
If question 1. is positive, is there an overhead in the first working code, due to the fact that the routine create an object of type OperatorType at every loop iteration?
Question 1 is negative.
Are there plans to introduce a feature like a compile-time for loop in the upcoming c++20?
I don't know C++20 enough to respond this question but I suppose not passing a set of function.
Anyway, you can do a sort of compile-time for loop using std::make_index_sequence/std::index_sequence starting from C++14.
By example, if you accept to extract the touple value outside your myprint() function, you can wrap it inside a lambda and write something as follows (using also C++17 template folding; in C++14 is a little more complicated)
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <typename T>
void myprint (T const & t)
{ std::cout << t << " "; }
template <std::size_t start, std::size_t ... Is, typename F, typename ... Ts>
void ctf_helper (std::index_sequence<Is...>, F f, std::tuple<Ts...> const & t)
{ (f(std::get<start + Is>(t)), ...); }
template <std::size_t start, std::size_t end, typename F, typename ... Ts>
void compile_time_for (F f, std::tuple<Ts...> const & t)
{ ctf_helper<start>(std::make_index_sequence<end-start>{}, f, t); }
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3>([](auto const & v){ myprint(v); }, x);
return 0;
}
If you really want extract the tuple element (or tuples elements) inside the function, the best I can imagine is transform your first example as follows
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <std::size_t start, template <std::size_t> class OT,
std::size_t ... Is, typename... Args>
void ctf_helper (std::index_sequence<Is...> const &, Args && ... args)
{ (OT<start+Is>{}(std::forward<Args>(args)...), ...); }
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
void compile_time_for (Args && ... args)
{ ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...); }
template <std::size_t I>
struct print_tuple_i
{
template <typename ... U>
void operator() (std::tuple<U...> const & x)
{ std::cout << std::get<I>(x) << " "; }
};
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0u, 3u, print_tuple_i>(x);
return 0;
}
-- EDIT --
The OP asks
Is there some advantage of using index_sequence over my first code?
I'm not an expert but this way you avoid recursion.
Compilers have recursion limits, from the template point of view, that can be strict. This way you avoid they.
Also, your code does not compile if you set the template parameters end > start. (One can imagine a situation where you want the compiler to determine if a loop is instantiated at all)
I suppose you mean that my code does not compile if start > end.
The bad part is that there aren't check about this problem so the compiler try to compile my code also in this case; so encounter
std::make_index_sequence<end-start>{}
where end - start is a negative number but used by a template that expect an unsigned number. So end - start become a very great positive number and this can cause problems.
You can avoid this problem imposing a static_assert() inside compile_time_for()
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
void compile_time_for (Args && ... args)
{
static_assert( end >= start, "start is bigger than end");
ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...);
}
Or maybe you can use SFINAE to disable the function
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
std::enable_if_t<(start <= end)> compile_time_for (Args && ... args)
{ ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...); }
If you want, using SFINAE you can add an overloaded compile_time_for() version to manage the end < start case
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename ... Args>
std::enable_if_t<(start > end)> compile_time_for (Args && ...)
{ /* manage the end < start case in some way */ }
I'll answer on the question how to fix your last code sample.
The reason why it doesn't compile is here:
template <int start, int end, template <int, typename...> class F, typename... Args>
void compile_time_for(F f, Args... args)
/\
F is a template, you can't have an object of a template class without template parameters being substituted. E.g. you can't have on object of std::vector type, but can have object of std::vector<int>. I suggest you to make F functor with a template operator() :
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <int start, int end, typename F, typename... Args>
void compile_time_for(F f, Args... args)
{
if constexpr (start < end)
{
f.template operator()<start>(std::forward<Args>(args)...);
compile_time_for<start + 1, end>(f, std::forward<Args>(args)...);
}
}
struct myprint
{
template <int I, typename... U>
void operator()(const std::tuple<U...>& x) { std::cout << std::get<I>(x) << " "; }
};
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3>(myprint(), x);
return 0;
}
Background
I'm trying to write some template functions for a template-only unit test library, specifically for Qt.
Problem
In this library, I have a variadic template that receives a variable amount of objects and functors (Qt5 Signals actually), always paired next to each other, as in QObject, signal, etc... then desirably followed by a variable amount of signal arguments.
Desired Solution
// implementation.h
template <typename T, typename U, typename... Sargs, typename... Fargs>
void test_signal_daisy_chain(T* t, void(T::*t_signal)(Fargs...),
U* u, void(U::*u_signal)(Fargs...),
Sargs... sargs,
Fargs... fargs) {...}
// client.cpp
test_signal_daisy_chain(object, &Object::signal1,
object, &Object::signal2,
object, &Object::signal3,
1, 2, 3); // where the signals are defined as void(Object::*)(int, int, int)
Where Fargs... corresponds to both the parameters in t_signal and u_signal as well as the arguments to pass to this function for testing, and Sargs... corresponds to a variable amount of QObject and signal member functions (void(T::*)(Fargs...)) to emit for the express purpose of testing.
Unsurprisingly I get "no matching function" due to "template argument deduction/substitution failed", and my ClangCodeModel plugin warns me that 6 arguments were expected, where 8 were given.
Working (ugly) solution
// implementation.h
template <typename... Fargs>
struct wrapper
{
template <typename T, typename U, typename... Sargs>
void test_signal_daisy_chain(Fargs... fargs,
T* t, void(T::*t_signal)(Fargs...),
U* u, void(U::*u_signal)(Fargs...),
Sargs... sargs) {...}
// client.cpp
wrapper<int, int, int>::test_signal_daisy_chain(1, 2, 3,
object, &Object::signal1,
object, &Object::signal2,
object, &Object::signal3);
I'm not content with having to explicitly define the variable function arguments at both the beginning of the function call and in the wrapper template type parameters. In fact, I was initially surprised that the could not be deduced simply by the fact that they were to match the variable arguments of the functors. I'm open to using wrapper functions as opposed to wrapper classes, as I already have a detail namespace set up which I'm willing to get messy for in order to provide a clean and user-friendly API.
Note: signal arguments can be anywhere from primitives to user-defined types to POD structs to template classes, all of variable length.
Edit 1: c++11 is a hard requirement so you can leave >c++11 features in your answer as long as they have some c++11 workaround, i.e. auto... is easy to fix, auto myFunction = []() constexpr {...}; much less so. If using if constexpr instead of a recursive template <std::size_t> helper function saves space and provides for a more succinct, complete, and future-proof answer, then please opt for whichever standard you deem best.
The simplest approach is to pack the parameters into a tuple at the beginning, and pass the tuple to test_signal_daisy_chain_impl:
template < typename... Fargs,
typename T, typename... Sargs>
void test_signal_daisy_chain_impl(const std::tuple<Fargs...> & fargs,
T* t, void(T::*t_signal)(Fargs...),
Sargs &&... sargs)
{
// apply unpacks the tuple
std::apply([&](auto ...params)
{
(t->*t_signal)(params...);
}, fargs);
// Although packed into the tuple, the elements in
// the tuple were not removed from the parameter list,
// so we have to ignore a tail of the size of Fargs.
if constexpr (sizeof...(Sargs) > sizeof...(Fargs))
test_signal_daisy_chain_impl(fargs, std::forward<Sargs>(sargs)...);
}
// Get a tuple out of the last I parameters
template <std::size_t I, typename Ret, typename T, typename... Qargs>
Ret get_last_n(T && t, Qargs && ...qargs)
{
static_assert(I <= sizeof...(Qargs) + 1,
"Not enough parameters to pass to the signal function");
if constexpr(sizeof...(Qargs)+1 == I)
return {std::forward<T>(t), std::forward<Qargs>(qargs)...};
else
return get_last_n<I, Ret>(std::forward<Qargs>(qargs)...);
}
template <typename T, typename... Fargs,
typename... Qargs>
void test_signal_daisy_chain(T* t, void(T::*t_signal)(Fargs...),
Qargs&&... qargs)
{
static_assert((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0,
"Expecting even number of parameters for object-signal pairs");
if constexpr ((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0) {
auto fargs = get_last_n<sizeof...(Fargs), std::tuple<Fargs...>>(
std::forward<Qargs>(qargs)...);
test_signal_daisy_chain_impl(fargs, t, t_signal,
std::forward<Qargs>(qargs)...);
}
}
And the usage:
class Object {
public:
void print_vec(const std::vector<int> & vec)
{
for (auto elem: vec) std::cout << elem << ", ";
}
void signal1(const std::vector<int> & vec)
{
std::cout << "signal1(";
print_vec(vec);
std::cout << ")\n";
}
void signal2(const std::vector<int> & vec)
{
std::cout << "signal2(";
print_vec(vec);
std::cout << ")\n";
}
void signal_int1(int a, int b)
{ std::cout << "signal_int1(" << a << ", " << b << ")\n"; }
void signal_int2(int a, int b)
{ std::cout << "signal_int2(" << a << ", " << b << ")\n"; }
void signal_int3(int a, int b)
{ std::cout << "signal_int3(" << a << ", " << b << ")\n"; }
};
int main()
{
Object object;
test_signal_daisy_chain(&object, &Object::signal1,
&object, &Object::signal2 ,
std::vector{1,2,3});
test_signal_daisy_chain(&object, &Object::signal_int1,
&object, &Object::signal_int2 ,
&object, &Object::signal_int3,
1,2);
}
Edit 1
Since C++11 is a hard constraint, there is a much uglier solution, based on the same principles. Things like std::apply and std::make_index_sequence have to be implemented. Overloading is used instead of if constexpr(....) :
template <std::size_t ...I>
struct indexes
{
using type = indexes;
};
template<std::size_t N, std::size_t ...I>
struct make_indexes
{
using type_aux = typename std::conditional<
(N == sizeof...(I)),
indexes<I...>,
make_indexes<N, I..., sizeof...(I)>>::type;
using type = typename type_aux::type;
};
template <typename Tuple, typename T, typename Method, std::size_t... I>
void apply_method_impl(
Method t_signal, T* t, const Tuple& tup, indexes<I...>)
{
return (t->*t_signal)(std::get<I>(tup)...);
}
template <typename Tuple, typename T, typename Method>
void apply_method(const Tuple & tup, T* t, Method t_signal)
{
apply_method_impl(
t_signal, t, tup,
typename make_indexes<
std::tuple_size<Tuple>::value>::type{});
}
template < typename... Fargs, typename... Sargs>
typename std::enable_if<(sizeof...(Fargs) == sizeof...(Sargs)), void>::type
test_signal_daisy_chain_impl(const std::tuple<Fargs...> & ,
Sargs &&...)
{}
template < typename... Fargs,
typename T, typename... Sargs>
void test_signal_daisy_chain_impl(const std::tuple<Fargs...> & fargs,
T* t, void(T::*t_signal)(Fargs...),
Sargs &&... sargs)
{
apply_method(fargs, t, t_signal);
// Although packed into the tuple, the elements in
// the tuple were not removed from the parameter list,
// so we have to ignore a tail of the size of Fargs.
test_signal_daisy_chain_impl(fargs, std::forward<Sargs>(sargs)...);
}
// Get a tuple out of the last I parameters
template <std::size_t I, typename Ret, typename T, typename... Qargs>
typename std::enable_if<sizeof...(Qargs)+1 == I, Ret>::type
get_last_n(T && t, Qargs && ...qargs)
{
return Ret{std::forward<T>(t), std::forward<Qargs>(qargs)...};
}
template <std::size_t I, typename Ret, typename T, typename... Qargs>
typename std::enable_if<sizeof...(Qargs)+1 != I, Ret>::type
get_last_n(T && , Qargs && ...qargs)
{
static_assert(I <= sizeof...(Qargs) + 1, "Not enough parameters to pass to the singal function");
return get_last_n<I, Ret>(std::forward<Qargs>(qargs)...);
}
template <typename T, typename... Fargs,
typename... Qargs>
void test_signal_daisy_chain(T* t, void(T::*t_signal)(Fargs...),
Qargs&&... qargs)
{
static_assert((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0,
"Expecting even number of parameters for object-signal pairs");
auto fargs = get_last_n<sizeof...(Fargs), std::tuple<Fargs...>>(
std::forward<Qargs>(qargs)...);
test_signal_daisy_chain_impl(fargs, t, t_signal,
std::forward<Qargs>(qargs)...);
}
Edit 2
It is possible to avoid runtime recursion by storing all parameters in a tuple. The following test_signal_daisy_chain_flat() does exactly that, while retaining the same interface as test_signal_daisy_chain():
template <typename Fargs, typename Pairs, std::size_t ...I>
void apply_pairs(Fargs && fargs, Pairs && pairs, const indexes<I...> &)
{
int dummy[] = {
(apply_method(std::forward<Fargs>(fargs),
std::get<I*2>(pairs),
std::get<I*2+1>(pairs)),
0)...
};
(void)dummy;
}
template <typename T, typename... Fargs,
typename... Qargs>
void test_signal_daisy_chain_flat(T* t, void(T::*t_signal)(Fargs...),
Qargs&&... qargs)
{
static_assert((sizeof...(Qargs) - sizeof...(Fargs)) % 2 == 0,
"Expecting even number of parameters for object-signal pairs");
auto fargs = get_last_n<sizeof...(Fargs), std::tuple<Fargs...>>(
std::forward<Qargs>(qargs)...);
std::tuple<T*, void(T::*)(Fargs...), const Qargs&...> pairs{
t, t_signal, qargs...};
apply_pairs(fargs, pairs,
typename make_indexes<(sizeof...(Qargs) - sizeof...(Fargs))/2>
::type{});
}
Caveats:
Not asserting that parameter pairs match. The compiler simply fails to compile (possibly deep in recursion).
The types of the parameters passed to the function are deduced from the signature of the first function, regardless of the types of the trailing parameters - the trailing parameters are converted to the required types.
All functions are required to have the same signature.
template<class T>
struct tag_t { using type=T; };
template<class Tag>
using type_t = typename Tag::type;
template<class T>
using no_deduction = type_t<tag_t<T>>;
template <typename T, typename U, typename... Sargs, typename... Fargs>
void test_signal_daisy_chain(
T* t, void(T::*t_signal)(Sargs...),
U* u, void(U::*u_signal)(Fargs...),
no_deduction<Sargs>... sargs,
no_deduction<Fargs>... fargs)
I assume the Fargs... in t_signal was a typo, and was supposed to be Sargs.
If not, you are in trouble. There is no rule that "earlier deduction beats later deduction".
One thing you can do in c++14 is to have a function returning a function object:
template <typename T, typename U, typename... Fargs>
auto test_signal_daisy_chain(
T* t, void(T::*t_signal)(Fargs...),
U* u, void(U::*u_signal)(Fargs...),
no_deduction<Fargs>... fargs
) {
return [=](auto...sargs) {
// ...
};
}
Then use looks like:
A a; B b;
test_signal_daisy_chain( &a, &A::foo, &b, &B::bar, 1 )('a', 'b', 'c');
doing this in c++11 is possible with a manually written function object.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I expand a tuple into variadic template function's arguments?
“unpacking” a tuple to call a matching function pointer
In C++11 templates, is there a way to use a tuple as the individual args of a (possibly template) function?
Example:
Let's say I have this function:
void foo(int a, int b)
{
}
And I have the tuple auto bar = std::make_tuple(1, 2).
Can I use that to call foo(1, 2) in a templaty way?
I don't mean simply foo(std::get<0>(bar), std::get<1>(bar)) since I want to do this in a template that doesn't know the number of args.
More complete example:
template<typename Func, typename... Args>
void caller(Func func, Args... args)
{
auto argtuple = std::make_tuple(args...);
do_stuff_with_tuple(argtuple);
func(insert_magic_here(argtuple)); // <-- this is the hard part
}
I should note that I'd prefer to not create one template that works for one arg, another that works for two, etc…
Try something like this:
// implementation details, users never invoke these directly
namespace detail
{
template <typename F, typename Tuple, bool Done, int Total, int... N>
struct call_impl
{
static void call(F f, Tuple && t)
{
call_impl<F, Tuple, Total == 1 + sizeof...(N), Total, N..., sizeof...(N)>::call(f, std::forward<Tuple>(t));
}
};
template <typename F, typename Tuple, int Total, int... N>
struct call_impl<F, Tuple, true, Total, N...>
{
static void call(F f, Tuple && t)
{
f(std::get<N>(std::forward<Tuple>(t))...);
}
};
}
// user invokes this
template <typename F, typename Tuple>
void call(F f, Tuple && t)
{
typedef typename std::decay<Tuple>::type ttype;
detail::call_impl<F, Tuple, 0 == std::tuple_size<ttype>::value, std::tuple_size<ttype>::value>::call(f, std::forward<Tuple>(t));
}
Example:
#include <cstdio>
int main()
{
auto t = std::make_tuple("%d, %d, %d\n", 1,2,3);
call(std::printf, t);
}
With some extra magic and using std::result_of, you can probably also make the entire thing return the correct return value.
Create an "index tuple" (a tuple of compile-time integers) then forward to another function that deduces the indices as a parameter pack and uses them in a pack expansion to call std::get on the tuple:
#include <redi/index_tuple.h>
template<typename Func, typename Tuple, unsigned... I>
void caller_impl(Func func, Tuple&& t, redi::index_tuple<I...>)
{
func(std::get<I>(t)...);
}
template<typename Func, typename... Args>
void caller(Func func, Args... args)
{
auto argtuple = std::make_tuple(args...);
do_stuff_with_tuple(argtuple);
typedef redi::to_index_tuple<Args...> indices;
caller_impl(func, argtuple, indices());
}
My implementation of index_tuple is at https://gitlab.com/redistd/redistd/blob/master/include/redi/index_tuple.h
but it relies on template aliases so if your compiler doesn't support that you'd need to modify it to use C++03-style "template typedefs" and replace the last two lines of caller with
typedef typename redi::make_index_tuple<sizeof...(Args)>::type indices;
caller_impl(func, argtuple, indices());
A similar utility was standardised as std::index_sequence in C++14 (see index_seq.h for a standalone C++11 implementation).
I'd like to write a function template, apply, which receives some function f, an integer i, and a parameter pack. apply needs to unpack the parameters and apply f to them, except for the ith parameter, pi. For pi, it needs to call some other function g before passing it as a parameter to f.
It seems that I need a way to partition the parameter pack into a left side, the ith parameter, and the right side. Is this possible? In code:
template<int i, typename Function, typename... Parms>
void apply(Function f, Parms... parms)
{
auto lhs = // what goes here?
auto pi = // what goes here?
auto rhs = // what goes here?
f(lhs..., g(pi), rhs...);
}
OK, here we go! It really ugly but I couldn't come up with a nicer version in a hurry ;) Most of the stuff is bog standard template specialization. The biggest issue is creating a list of integers of the proper size. I seem to recall that I came up with a nice version but somehow I can't recall what I did. Enjoy!
#include <iostream>
#include <utility>
// printing the values
void print_args() {}
template <typename F> void print_args(F f) { std::cout << f; }
template <typename F, typename... T>
void print_args(F f, T... args)
{
std::cout << f << ", ";
print_args(args...);
}
// the function object to be called:
struct Functor
{
template <typename... T>
void operator()(T... args)
{
std::cout << "f(";
print_args(args...);
std::cout << ")\n";
}
};
// conditionally apply g():
template <typename T> T g(T value) { return 1000 + value; }
template <int i, int j, typename T>
typename std::enable_if<i != j, T>::type forward(T t) { return t; }
template <int i, int j, typename T>
typename std::enable_if<i == j, T>::type forward(T t) { return g(t); }
// create a series of integers:
template <int... Values> struct values {};
template <int Add, typename> struct combine_values;
template <int Add, int... Values>
struct combine_values<Add, values<Values...>>
{
typedef values<Values..., Add> type;
};
template <int Size> struct make_values;
template <> struct make_values<0> { typedef values<> type; };
template <int Size>
struct make_values
{
typedef typename combine_values<Size, typename make_values<Size -1>::type>::type type;
};
// applying f(t...) except for ti where g(ti) is called
template <int i, int... Values, typename Function, typename... T>
void apply_aux(values<Values...>, Function f, T... t)
{
f(forward<i, Values>(t)...);
}
template <int i, typename Function, typename... T>
void apply(Function f, T... t)
{
apply_aux<i>(typename make_values<sizeof...(T)>::type(), f, t...);
}
int main()
{
apply<3>(Functor(), 1, 2, 3, 4, 5, 6, 7, 8);
apply<4>(Functor(), 1, 2, 3, 4, 5, 6, 7, 8);
apply<5>(Functor(), 1, 2, 3, 4, 5, 6, 7, 8);
}
Iactually did code something similar a little while ago. So try the following code:
template<unsigned N, unsigned M>
struct call_up_impl{
template<class Func, class Mutator, class Tuple, class... Args>
static void do_call(const Func& func, const Mutator& mutator, const Tuple& args, Args&&... unpacked_args) {
call_up_impl<N-1, M>::do_call(func, mutator, args, std::get<N-1>(args), std::forward<Args>(unpacked_args)...);
}
};
template<unsigned M>
struct call_up_impl<0, M> {
template<class Func, class Mutator, class Tuple, class... Args>
static void do_call(const Func& func, const Mutator&, const Tuple&, Args&&... unpacked_args) {
func(std::forward<Args>(unpacked_args)...);
}
};
template<unsigned M>
struct call_up_impl<M, M> {
template<class Func, class Mutator, class Tuple, class... Args>
static void do_call(const Func& func, const Mutator& mutator, const Tuple& args, Args&&... unpacked_args) {
call_up_impl<M-1, M>::do_call(func, mutator, args, mutator(std::get<M-1>(args)), std::forward<Args>(unpacked_args)...);
}
};
template<int i, typename Function, typename... Parms>
void apply(Function f, Parms... parms) {
std::tuple<Parms...> t(parms...);
call_up_impl<std::tuple_size<decltype(t)>::value, i + 1>::do_call(f, &g, t);
}
This is a quick adaption of my original code, so it isn't thoroughly tested and maybe not the not optimal way to do this, but it should work at least (at least according to a quick test and depending what exactly you want). It should be possible to do this without the tuple, but I haven't gotten that to compile with g++ (it doesn't seem to like the nested variadic templates needed). However changing apply to:
template<int i, typename Function, typename... Parms>
void apply(Function f, Parms&&... parms) {
std::tuple<Parms&&...> t(std::forward<Parms>(parms)...);
call_up_impl<std::tuple_size<decltype(t)>::value, i + 1>::do_call(f, &g, t);
}
will probably avoid most of the overhead introduced by the tuple. It would be even better to make correct forwarding of the results of the std::get calls, but I'm too tired to work that out write now.