I am trying to learn about variadic templates in C++11. I have a class which is basically a wrapper around a std::array
. I want to be able to pass function objects (ideally lambdas) to a member function and then have the elements of the std::array
passed on as parameters of the function object.
I have used a static_assert
to check that the number of parameters matches the length of the array but I cannot think of a way to pass the elements as arguments.
Here is the code
#include <iostream>
#include <array>
#include <memory>
#include <initializer_list>
using namespace std;
template<int N, typename T>
struct Container {
template<typename... Ts>
Container(Ts&&... vs) : data{{std::forward<Ts>(vs)...}} {
static_assert(sizeof...(Ts)==N,"Not enough args supplied!");
}
template< typename... Ts>
void doOperation( std::function<void(Ts...)>&& func )
{
static_assert(sizeof...(Ts)==N,"Size of variadic template args does not match array length");
// how can one call func with the entries
// of data as the parameters (in a way generic with N)
}
std::array<T,N> data;
};
int main(void)
{
Container<3,int> cont(1,2,3);
double sum = 0.0;
auto func = [&sum](int x, int y, int z)->void{
sum += x;
sum += y;
sum += z;
};
cont.doOperation(std::function<void(int,int,int)>(func));
cout << sum << endl;
return 0;
}
So my question (as indicated in the code) is how can one pass the entries of data
onto the function func
in a way which is generic with N
?
Bonus Question: Is it possible to do away with the unsightly conversion to std::function
in main and pass in a lambda directly?
It takes one fixed argument and then any number of arguments can be passed. The variadic function consists of at least one fixed variable and then an ellipsis(…) as the last parameter. This enables access to variadic function arguments. *argN* is the last fixed argument in the variadic function.
A variadic template is a class or function template that supports an arbitrary number of arguments. This mechanism is especially useful to C++ library developers: You can apply it to both class templates and function templates, and thereby provide a wide range of type-safe and non-trivial functionality and flexibility.
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.
variadic (not comparable) (computing, mathematics, linguistics) Taking a variable number of arguments; especially, taking arbitrarily many arguments.
Given the well-known indices infrastructure:
namespace detail
{
template<int... Is>
struct seq { };
template<int N, int... Is>
struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };
template<int... Is>
struct gen_seq<0, Is...> : seq<Is...> { };
}
You could redefine your class template this way:
template<int N, typename T>
struct Container {
template<typename... Ts>
Container(Ts&&... vs) : data{{std::forward<Ts>(vs)...}} {
static_assert(sizeof...(Ts)==N,"Not enough args supplied!");
}
template<typename F>
void doOperation(F&& func)
{
doOperation(std::forward<F>(func), detail::gen_seq<N>());
}
template<typename F, int... Is>
void doOperation(F&& func, detail::seq<Is...>)
{
(std::forward<F>(func))(data[Is]...);
}
std::array<T,N> data;
};
Here is a live example.
Notice, that you do not need to construct an std::function
object in main()
: the std::function
can be implicitly constructed from the lambda. However, you do not even need to use std::function
at all here, possibly incurring in an unnecessary run-time overhead.
In the solution above, I just let the type of the callable object to be a template parameter that can be deduced by the compiler.
You can use this utility template to create a sequence of indices at compile time:
template< std::size_t... Ns >
struct indices {
typedef indices< Ns..., sizeof...( Ns ) > next;
};
template< std::size_t N >
struct make_indices {
typedef typename make_indices< N - 1 >::type::next type;
};
template<>
struct make_indices< 0 > {
typedef indices<> type;
};
Then make a caller function that takes indices
as a parameter so you got a way of deducing the indices sequence:
template<typename... Ts, size_t...Is>
void call(std::function<void(Ts...)>&& func, indices<Is...>)
{
func( data[Is]... );
}
And then you can call it like this:
template< typename... Ts>
void doOperation( std::function<void(Ts...)>&& func )
{
static_assert(sizeof...(Ts)==N,"Size of variadic template args does not match array length");
call( std::forward<std::function<void(Ts...)>>(func), typename make_indices<N>::type() );
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With