It's definitely possible, but you cannot determine the safety of doing it at compile time. This is, as WhozCraig says, because the vector lacks a compile-time size.
I'm still trying to earn my template meta programming wings, so I may have done things a little unusually. But the core idea here is to have a function template recursively invoke itself with the next item in the vector until it has built up a parameter pack with the desired parameters. Once it has that, it's easy to pass it to the function in question.
The implementation of the core here is in apply_first_n
, which accepts a target std::function<R(Ps...)>
, and a vector, and a parameter pack of Ts...
. When Ts...
is shorter than Ps...
it builds up the pack; once it's the same size, it passes it to the function.
template <typename R, typename... Ps, typename... Ts>
typename std::enable_if<sizeof...(Ps) == sizeof...(Ts), R>::type
apply_first_n(std::function<R(Ps...)> f, const std::vector<int> &v, Ts&&... ts)
{
if (sizeof...(Ts) > v.size())
throw std::out_of_range("vector too small for function");
return f(std::forward<Ts>(ts)...);
}
template <typename R, typename... Ps, typename... Ts>
typename std::enable_if<sizeof...(Ps) != sizeof...(Ts), R>::type
apply_first_n(std::function<R(Ps...)> f, const std::vector<int> &v, Ts&&... ts)
{
const int index = sizeof...(Ps) - sizeof...(Ts) - 1;
static_assert(index >= 0, "incompatible function parameters");
return apply_first_n(f, v, *(std::begin(v) + index), std::forward<Ts>(ts)...);
}
You call this with, e.g., apply_first_n(std::function<int(int, int)>(f), v);
. In the live example, make_fn
just makes the conversion to std::function
easier, and ProcessInts
is a convenient testing function.
I'd love to figure out how to avoid the use of std::function
, and to repair any other gross inefficiencies that exist. But I'd say this is proof that it's possible.
For reference, I took the above approach further, handling set
, vector
, tuple
, and initializer_list
, as well as others that match the right interfaces. Removing std::function
seemed to require the func_info
traits class, as well as several overloads. So while this extended live example is definitely more general, I'm not sure I'd call it better.