Compile time sort of heterogenous tuples - c++

I know it's possible to use the C++ type system to generate a sorted type list from an existing tuple type.
Examples of doing this can be found at:
https://codereview.stackexchange.com/questions/131194/selection-sorting-a-type-list-compile-time
How to order types at compile-time?
However, is it possible to do a compile-time sort of a heterogeneous tuple by value? For example:
constexpr std::tuple<long, int, float> t(2,1,3);
constexpr std::tuple<int, long, float> t2 = tuple_sort(t);
assert(t2 == std::tuple<int, long, float>(1,2,3));
My assumption is this is not possible, since you'd have to conditionally generate new tuple types based on the result of comparing values. Even if the comparison function uses constexpr, it would seem that this can't work.
However, an offhand comment from this answer indicates it somehow is possible to do this, just very difficult:
I lied. You can do it if the values and the compare function are
constexpr, but the code to pull it off will be huge and not worth the
time to write.
So is this comment correct? How could this even be conceptually possible, given the way the C++ type system works.

To preface the answer, it might be vastly more straightforward to use Boost.Hana. The prerequisite for Hana is that your comparison produces a compile-time answer. In your case, this would require a Hana tuple containing compile-time versions of these basic data types, similar to std::integral_constant. If it's acceptable to have your tuples' values encoded entirely in their types, Hana makes this trivial.
I believe it would be possible to do this directly once you can use a tuple as a non-type template parameter in C++20. Until then, you can get pretty close (live example):
int main() {
constexpr std::tuple<long, int, float> t(2,1,3);
call_with_sorted_tuple(t, [](const auto& sorted) {
assert((sorted == std::tuple<int, long, float>(1,2,3)));
});
}
As far as I know, it is impossible to return the sorted tuple directly; the callback approach is required because it is instantiated with every possible tuple type and only the correct one is actually run. This means there is significant compile-time overhead to this approach. The compile times grow quickly with small tuple size increases.
Now, how does this actually work? Let's get the magic out of the way—converting a runtime integral value into a compile-time one. This can fit well into its own header, and is shamelessly stolen from P0376:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0376r0.html
#include <array>
#include <type_traits>
#include <utility>
// A function that invokes the provided function with
// a std::integral_constant of the specified value and offset.
template <class ReturnType, class T, T Value, T Offset, class Fun>
constexpr ReturnType invoke_with_constant_impl(Fun&& fun) {
return std::forward<Fun>(fun)(
std::integral_constant<T, Value + Offset>());
}
// Indexes into a constexpr table of function pointers
template <template <class...> class ReturnTypeDeducer,
class T, T Offset, class Fun, class I, I... Indices>
constexpr decltype(auto) invoke_with_constant(Fun&& fun, T index,
std::integer_sequence<I, Indices...>) {
// Each invocation may potentially have a different return type, so we
// need to use the ReturnTypeDeducer to figure out what we should
// actually return.
using return_type
= ReturnTypeDeducer<
decltype(std::declval<Fun>()(std::integral_constant<T, Indices + Offset>()))...>;
return std::array<return_type(*)(Fun&&), sizeof...(Indices)>{
{{invoke_with_constant_impl<return_type, T, Indices, Offset, Fun>}...}}
[index - Offset](std::forward<Fun>(fun));
}
template <class T, T BeginValue, T EndValue>
struct to_constant_in_range_impl {
// Instantiations of "type" are used as the Provider
// template argument of argument_provider.
template <class U>
struct type
{
template <template <class...> class ReturnTypeDeducer, class Fun, class Self>
static constexpr decltype(auto) provide(Fun&& fun, Self&& self) {
return invoke_with_constant<ReturnTypeDeducer, T, BeginValue>(
std::forward<Fun>(fun),
std::forward<Self>(self).value,
std::make_index_sequence<EndValue - BeginValue>());
}
U&& value;
};
};
Now one thing to note is that I use C++20's ability to give lambdas template parameters simply because compilers already support this and it makes turning index_sequences into parameter packs really easy. The long way to do this is available prior to C++20, but a bit of an eyesore on top of code that's already hard enough to get through.
The sort itself isn't too bad despite the tuple needing compile-time indices for std::get (unless you reuse the above magic, but all I have to say to that is yikes). You can change the algorithm as needed. You can even use a regular std::vector in C++20 and push indices onto the back. What I chose to do is generate a std::array containing the sorted-order indices of the tuple:
// I had trouble with constexpr std::swap library support on compilers.
template<typename T>
constexpr void constexpr_swap(T& a, T& b) {
auto temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}
template<std::size_t I>
using index_c = std::integral_constant<std::size_t, I>;
template<typename... Ts>
constexpr auto get_index_order(const std::tuple<Ts...> tup) {
return [&]<std::size_t... Is>(std::index_sequence<Is...> is) {
std::array<std::size_t, sizeof...(Is)> indices{Is...};
auto do_swap = [&]<std::size_t I, std::size_t J>(index_c<I>, index_c<J>) {
if (J <= I) return;
if (std::get<I>(tup) < std::get<J>(tup)) return;
constexpr_swap(indices[I], indices[J]);
};
auto swap_with_min = [&]<std::size_t I, std::size_t... Js>(index_c<I> i, std::index_sequence<Js...>) {
(do_swap(i, index_c<Js>{}), ...);
};
(swap_with_min(index_c<Is>{}, is), ...);
return indices;
}(std::index_sequence_for<Ts...>{});
}
The main idea here is obtaining a pack of indices from 0 to N-1 and then dealing with each individually. Rather than trying to generate a second pack from I+1 to N-1, I took the easy road and reused the 0 to N-1 pack I already had, ignoring all out-of-order combinations when swapping. The dance with index_c is to avoid calling the lambdas via awkward lambda.template operator()<...>(...) syntax.
Now we have the indices of the tuple in sorted order and magic to convert one index to one with its value encoded in the type. Rather than build the magic to handle multiple values, I took the probably-suboptimal approach to build on the support for one at a time by making a recursive function:
template<typename... Ts, typename F, std::size_t... Converted>
constexpr void convert_or_call(const std::tuple<Ts...> tup, F f, const std::array<std::size_t, sizeof...(Ts)>& index_order, std::index_sequence<Converted...>) {
using Range = typename to_constant_in_range_impl<std::size_t, 0, sizeof...(Ts)>::template type<const std::size_t&>;
if constexpr (sizeof...(Converted) == sizeof...(Ts)) {
f(std::tuple{std::get<Converted>(tup)...});
} else {
Range r{index_order[sizeof...(Converted)]};
r.template provide<std::void_t>([&]<std::size_t Next>(index_c<Next>) {
convert_or_call(tup, f, index_order, std::index_sequence<Converted..., Next>{});
}, r);
}
}
I would have made this a lambda to avoid repeating captures, but as its recursive, it needs a workaround to call itself in lambda form. I'd be happy to hear of a good, constexpr-compatible solution for lambdas in this case that takes into account the fact that the lambda's template arguments differ each call.
Anyway, this is the use of the magic. We want to call this a total of N times, where N is the tuple size. That's what the if constexpr checks for, and finally delegates to the function passed from main, easily building a new tuple from the compile-time index order sequence. To recurse, we add on this compile-time index to a list we build up.
Finally, since what should have been a lambda is its own function, the function called from main is a simple wrapper that gets the index-order array and starts off the runtime-sequence-to-compile-time-sequence recursion with nothing converted to start:
template<typename... Ts, typename F>
constexpr void call_with_sorted_tuple(const std::tuple<Ts...>& tup, F f) {
auto index_order = get_index_order(tup);
convert_or_call(tup, f, index_order, std::index_sequence<>{});
}

I believe, it can not be done.
The essential part of any sorting would be to use tuple value in if constexpr context, but since function arguments are not constexpr, they can not appear in if constexpr.
And since tuples can't be non-type template arguments, template-based solution can't be implemented either. Unless we make tuple of type-encoded values (like std::integral_constant) I believe the solution is not available.

Return type cannot depend of value of parameters (even more as parameter cannot be constexpr) of a function, so
constexpr std::tuple<long, int, float> t1(2, 1, 3);
constexpr std::tuple<long, int, float> t2(3, 2, 1);
static_assert(std::is_same<decltype(tuple_sort(t1), decltype(tuple_sort(t2)>::value, "!");

Related

C++ Lambdas with Ellipses in the Parameter List

I'm working on a library which uses lambdas for delineating the scopes of expression terms. Because the library has to hand out unique integer numbers to identify each variable, it is ideal if the library, not the user, constructs the variables and the user code receives them as lambda arguments.
(In other words I am implementing a C++ analog of "call\fresh" from miniKanren.)
Since the user may want to introduce any number from zero to many fresh variables at a particular scope, I want the user to be able to pass lambdas with differing numbers of arguments to the library. However, I'm not aware of any (simple) way (in C++14) to deduce the number of parameters to an arbitrary lambda object.
An idea occurred to me why not pass a fixed number (say, 10) of variable-id arguments to the lambda, and have the user code use ellipses in the lambda to ignore the ones not needed? Something like this:
auto no_args = call_fresh([](...) { return success(); });
auto one_arg = call_fresh([](var A, ...) { return A == 1; });
auto two_args = call_fresh([](var A, var B, ...) { return A == 1 && B == 2; });
Compiler explorer seems to accept ellipses in lambda parameter lists, at least with gcc.
It would be called something like this (note how the code always passes 10 variable id's no matter whether "f" names only one, two, or none of them):
template <typename F>
auto call_fresh(F f)
{
return [f](StateCounter sc) {
return f(sc+0,sc+1,sc+2,sc+3,sc+4,
sc+5,sc+6,sc+7,sc+8,sc+9);
};
}
Granted it's a feature I was surprised exists, is there any reason not to use lambdas with ellipses?
However, I'm not aware of any (simple) way (in C++14) to deduce the number of parameters to an arbitrary lambda object.
It seems to me that you're looking for sizeof...() over a variadic auto list of paramenters
#include <iostream>
int main ()
{
auto l = [](auto ... as) { return sizeof...(as); };
std::cout << l(1, 2L, 3.0, 4.0f, "5") << std::endl; // print 5
}
Your lambdas are essentially C-style variadic functions. There's nothing wrong with using them, and if you don't want to access the values (which is somewhat ugly), that is fine.
However, the underlying problem that it seems like you actually want to solve is to let your library find the number of arguments (or arity) of a function/lambda/..., which you can do with template metaprogramming - no need for your users to work around that issue.
Disclosure: There is an implementation of this in a library that I also work on, here.
Here is a simple example:
template <typename Callable>
struct function_arity : public function_arity<decltype(&Callable::operator())>
{};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_arity<ReturnType(ClassType::*)(Args...) const>
{
constexpr static size_t arity = sizeof...(Args);
};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_arity<ReturnType(ClassType::*)(Args...)>
{
constexpr static size_t arity = sizeof...(Args);
};
The compiler will automatically deduce the argument types for you, and sizeof... will get you the number of arguments that you need.
Then, you can use function_arity<decltype(lambda)>::arity to get the number of arguments of your lambda. The last version deals with mutable lambdas, where the call operator is non-constant. You may also want to extend this to work properly with noexcept, or you will run into errors like this libc++ bug.
Unfortunately, this will not work with overloaded or templated operator() (e.g. if you use auto-type parameters in your lambda). If you also want to support functions instead of lambdas, additional specializations may be necessary.

How to avoid using the "indices trick" repeatedly?

I have a class named memory_region, which is sort of like an untyped gsl::span (i.e. it's essentially a void* and a size_t), which I also use for type erasure. It thus has an as_span<T>() method.
With this class, I have a std::unordered_map<std::string, memory_region> my_map - which is used to pass type-erased spans between parts of my code which don't share headers, so they can't know about each others' types. The typical access to one of these looks like:
auto foo = my_map.at("foo").as_span<bar_t>();
This works just fine with code that has a fixed set of buffers and types and names. But - things get tricky when my code's buffers depend on a template parameter pack. Now, I've implemented a
std::string input_buffer_name(unsigned input_buffer_index);
function, so if I have an index sequence and my parameter pack I can do, for example
template<typename Ts..., std::size_t... Indices>
my_function(std::unordered_map<std::string, memory_region>& my map) {
compute_stuff_with_buffers(
my_map.at(input_buffer_name(Indices)).as_span<Ts>()...
);
}
(this is a variation on the infamous indices trick; note that the same type may appear more than once in the pack, so I can't "wrap the types in a tuple" and acces it by type.)
The thing is, though - my code doesn't have that index sequence in the template parameters; most of it is templated on just the parameter pack of types. So I find myself writing "helper functions/methods" all the time to be able to use that index sequence, e.g.:
template<typename Ts..., std::size_t... Indices>
my_function_helper(
std::unordered_map<std::string, memory_region>& my map
std::index_sequence<Indices...> /* unused */)
{
compute_stuff_with_buffers(
my_map.at(input_buffer_name(Indices)).as_span<Ts>()...
);
}
template<typename Ts...>
my_function(std::unordered_map<std::string, memory_region>& my map) {
my_function_helper(
my_map, std::make_index_sequence<sizeof...(Ts)> {}
);
}
What can I do instead, that will not involve so much code duplication?
In this case you can use simple pack expansion in the form of an array:
template<typename... Ts>
void my_function(std::unordered_map<std::string, memory_region>& my_map) {
using swallow = int[];
unsigned i = 0;
(void)swallow{0, (my_map.at(input_buffer_name(i++)).as_span<Ts>(), 0)...};
}
Demo
The pack expansion will be expanded in order ([temp.variadic]), and also evaluated in order (left to right) because we're using a braced initializer list (an unused integer array): [dcl.init.aggr]
When an aggregate is initialized by an initializer list [...] the elements of the initializer list are taken as initializers for the elements of the aggregate, in order.
Re:
But what if I need to use input_buffer_name(i) twice? e.g. if I need to use
{ input_buffer_name(index), my_map.at(input_buffer_name(index).as_span<Ts>()) }
I suppose we could take advantage of the fact that logical AND will sequence left to right ([expr.log.and]), and also a boolean can be promoted to int:
template<typename... Ts>
void my_function_v2(std::unordered_map<std::string, memory_region>& my_map) {
using swallow = int[];
unsigned i = 0;
(void)swallow{0, ((std::cout<< input_buffer_name(i) << std::endl, true) && (my_map.at(input_buffer_name(i++)).as_span<Ts>(), true))...};
}
Demo 2

Need help to understand template function with complex typename parameters

I'm examining a Stroustroup's book "C++ Programming 4th edition". And I'm trying to follow his example on matrix design.
His matrix class heavily depends on templates and I try my best to figure them out.
Here is one of the helper classes for this matrix
A Matrix_slice is the part of the Matrix implementation that maps a
set of subscripts to the location of an element. It uses the idea
of generalized slices (§40.5.6):
template<size_t N>
struct Matrix_slice {
Matrix_slice() = default; // an empty matrix: no elements
Matrix_slice(size_t s, initializer_list<size_t> exts); // extents
Matrix_slice(size_t s, initializer_list<size_t> exts, initializer_list<siz e_t> strs);// extents and strides
template<typename... Dims> // N extents
Matrix_slice(Dims... dims);
template<typename... Dims,
typename = Enable_if<All(Convertible<Dims,size_t>()...)>>
size_t operator()(Dims... dims) const; // calculate index from a set of subscripts
size_t size; // total number of elements
size_t start; // star ting offset
array<size_t,N> extents; // number of elements in each dimension
array<size_t,N> strides; // offsets between elements in each dimension
};
I
Here are the lines that build up the subject of my question:
template<typename... Dims,
typename = Enable_if<All(Convertible<Dims,size_t>()...)>>
size_t operator()(Dims... dims) const; // calculate index from a set of subscripts
earlier in the book he describes how Enable_if and All() are implemented:
template<bool B,typename T>
using Enable_if = typename std::enable_if<B, T>::type;
constexpr bool All(){
return true;
}
template<typename...Args>
constexpr bool All(bool b, Args... args)
{
return b && All(args...);
}
I have enough information to understand how they work already and by looking at his Enable_if implementation I can deduce Convertible function as well:
template<typename From,typename To>
bool Convertible(){
//I think that it looks like that, but I haven't found
//this one in the book, so I might be wrong
return std::is_convertible<From, To>::value;
}
So, I can undersand the building blocks of this template function declaration
but I'm confused when trying to understand how they work altogather.
I hope that you could help
template<typename... Dims,
//so here we accept the fact that we can have multiple arguments like (1,2,3,4)
typename = Enable_if<All(Convertible<Dims,size_t>()...)>>
//Evaluating and expanding from inside out my guess will be
//for example if Dims = 1,2,3,4,5
//Convertible<Dims,size_t>()... = Convertible<1,2,3,4,5,size_t>() =
//= Convertible<typeof(1),size_t>(),Convertible<typeof(2),size_t>(),Convertible<typeof(3),size_t>(),...
//= true,true,true,true,true
//All() is thus expanded to All(true,true,true,true,true)
//=true;
//Enable_if<true>
//here is point of confusion. Enable_if takes two tamplate arguments,
//Enable_if<bool B,typename T>
//but here it only takes bool
//typename = Enable_if(...) this one is also confusing
size_t operator()(Dims... dims) const; // calculate index from a set of subscripts
So what do we get in the end?
This construct
template<typename ...Dims,typename = Enable_if<true>>
size_t operator()(Dims... dims) const;
The questions are:
Don't we need the second template argument for Enable_if
Why do we have assignment ('=') for a typename
What do we get in the end?
Update:
You can check the code in the same book that I'm referencing here
The C++ Programming Language 4th edition at page 841 (Matrix Design)
This is basic SFINAE. You can read it up here, for example.
For the answers, I'm using std::enable_if_t here instead of the EnableIf given in the book, but the two are identical:
As mentioned in the answer by #GuyGreer, the second template parameter of is defaulted to void.
The code can be read as a "normal" function template definition
template<typename ...Dims, typename some_unused_type = enable_if_t<true> >
size_t operator()(Dims... dims) const;
With the =, the parameter some_unused_type is defaulted to the type on the right-hand side. And as one does not use the type some_unused_type explicitly, one also does not need to give it a name and simply leave it empty.
This is the usual approach in C++ also found for function parameters. Check for example operator++(int) -- one does not write operator++(int i) or something like that.
What's happening all together is SFINAE, which is an abbreviation for Substitution Failure Is Not An Error. There are two cases here:
First, if the boolean argument of std::enable_if_t is false, one gets
template<typename ...Dims, typename = /* not a type */>
size_t operator()(Dims ... dims) const;
As there is no valid type on the rhs of typename =, type deduction fails. Due to SFINAE, however, it does not lead to a compile-time error but rather to a removal of the function from the overload set.
The result in practice is as if the function would have not been defined.
Second, if the boolean argument of std::enable_if_t is true, one gets
template<typename ...Dims, typename = void>
size_t operator()(Dims... dims) const;
Now typename = void is a valid type definition and so there is no need to remove the function. It can thus be normally used.
Applied to your example,
template<typename... Dims,
typename = Enable_if<All(Convertible<Dims,size_t>()...)>>
size_t operator()(Dims... dims) const;
the above means that this function exists only if All(Convertible<Dims,size_t>()... is true. This basically means the function parameters should all be integer indices (me personally, I would write that in terms of std::is_integral<T> however).
The missing constexprs notwithstanding, std::enable_if is a template that takes two parameters, but the second one is defaulted to void. It makes sense when writing up a quick alias to this to keep that convention.
Hence the alias should be defined as:
template <bool b, class T = void>
using Enable_if = typename std::enable_if<b, T>::type;
I have no insight into whether this default parameter is present in the book or not, just that this will fix that issue.
The assignment of a type is called a type alias and does what it says on the tin, when you refer to the alias, you're actually referring to what it aliases. In this case it means that when you write Enable_if<b> the compiler handily expands that to typename std::enable_if<b, void>::type for you, saving you all that extra typing.
What you get in the end is a function that is only callable if every parameter you passed to it is convertible to a std::size_t. This allows overloads of functions to be ignored if specific conditions are not met which is more a powerful technique than just matching types up for selecting what function to call. The link for std::enable_if has more information on why you would want to do that, but I warn beginners that this subject gets kinda heady.

Generate a std::tuple from standard container

Is there a portable way to generate a std::tuple (really a std::array) from the contents of a container? Such a tuple would allow std::apply to extract function arguments from a container.
My first attempt, using tail recursion, fails with compiler error: "recursive template instantiation exceeds maximum...".
I could not quite get my second attempt (std::for_each with a mutable lambda holding tuple) to compile with desired results.
I assume that something along the lines of how boost::mpl handles variadic metafunctions (ie magic using boost::preprocessor) could be made to work -- but that's so c++03. I'm hoping there's a better solution.
The function signature would look something like:
std::list<int> args_as_list = {1, 2, 3, 4};
auto tpl = args_as_tuple(args_as_list);
where type of tpl is std::array<int const, 4>.
Short answer: no, it is not possible.
Explanation: both std::tuple and std::array require compile-time information about number of elements. std::list or std::vector can provide only runtime information about elements count.
your args_as_tuple function would have to be a template, taking number of expected arguments as template argument (args_as_tuple<4>(args_as_list)).
Although having to put number of arguments as template argument seems harsh, but in case of your example it's is quite obvious - number of function arguments (function provided to std::apply) has to be known on compile-time as well.
For more generic code you could use: function-traits or code from this answer.
Or use std::array from begging instead of std::list (a lot of generic template code, but good compile time checks)
The number of elements in a std::tuple, or a std::array, is part of its type information. Therefore, your function args_as_tuple proposed above would have to be a template somehow, and each different possible size of the result is going to require a different instantiation of that template. Therefore, you cannot make a program that can support arbitrarily many sizes of tuples unless the code of that program is infinite (not possible).
If you only care about the range of values of int, say, you could instantiate the template 4 billion times, but then your executable is going to be at least 4 gigabytes large.
If you really only care about a few different sizes of vectors in your actual program, you could instantiate just those templates and write conversion code that cases out on the value of std::list::size() and calls the appropriate function (tedious).
But your exact code snippet
std::list<int> args_as_list = {1, 2, 3, 4};
auto tpl = args_as_tuple(args_as_list);
can never work in C++. Because, in C++ all variables have to have a known type determined at compile time. Even though you are using keyword auto, that auto has to get resolved at compile time to a fixed type, which means a fixed size if it's a tuple or array, no matter what sort of template shenanigans the expression args_as_tuple is doing.
Since my problem can't be solved, I solved a slightly different one which allowed me to move on.
I came up with a solution which allows me to extract arguments for a functor from a container. I am able to instantiate a eval_container with the functor I want evaluated, then pass the container to the resulting object.
#include <utility>
template <int N>
using Int = std::integral_constant<int, N>;
template <typename T>
struct arity : arity<decltype(&T::operator())> {};
template <typename T, typename RT, typename...Args>
struct arity<RT(T::*)(Args...) const>
{
// could enforce maximum number of arguments
static constexpr int value = sizeof...(Args);
};
template <typename F, int N = arity<F>::value>
struct eval_container
{
eval_container(F const& f) : f(f) {}
eval_container(F&& f) : f(std::move(f)) {}
template <typename Iter, typename I, typename...Args>
auto operator()(Iter&& iter, I, Args&&...args) const
{
// assert(iter != end)
auto&& arg = *iter++;
return (*this)(std::forward<Iter>(iter)
, Int<I()-1>{}
, std::forward<Args>(args)...
, arg);
}
template <typename Iter, typename...Args>
auto operator()(Iter&&, Int<0>, Args&&...args) const
{
// assert(iter == end)
return f(std::forward<Args>(args)...);
}
template <typename C>
auto operator()(C const& container) const
{
return (*this)(container.begin(), Int<N>{});
}
F f;
};
}

Expanding an STL container into a variadic template

To keep things generic and straightforward, say that I have a std::vector of integers, such as:
std::vector<int> v;
Now, what I am wondering is, is it possible to take n (where n is a constant known at compile time) values from v and pass them to an arbitrary function? I know that this is doable with variadic templates:
template<typename... T>
void pass(void (*func)(int, int, int), T... t) {
func(t...);
}
And then we hope 'pass' is called with exactly 3 integers. The details don't matter so much. What I am wondering is, is the following somehow doable:
void pass(void (*func)(int, int, int), std::vector<int> &t) {
auto iter = t.begin();
func((*iter++)...);
}
Where ... is being used like a variadic template? Essentially, I'm asking if I can
Expand a std::vector or other STL container into a variadic template with n elements
And/or in-order pass these values directly to a function being called
Is this possible with C++11? Noting that I need this to work on MSVC v120/VS2013.
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.