What's a clean way to generalise a template to pass a variable number of arguments (the count given as a template argument) to a function? For example the function permute
, below:
template<std::size_t N>
using byte_vector_t = uint8_t __attribute__((ext_vector_type(N)));
template <std::size_t N, int(*f)(int)>
byte_vector_t<N> permute(byte_vector_t<N> x, byte_vector_t<N> y) {
return __builtin_shufflevector(x, y, f(0), f(1), f(2), f(3), f(4), f(5), f(6), f(7), f(8), f(9), f(10), f(11), f(12), f(13), f(14), f(15) );
}
template <std::size_t N>
byte_vector_t<N> zip_lo(byte_vector_t<N> x, byte_vector_t<N> y) {
auto f = [](int i) -> int { return ((i & 1) ? N : 0) + (i / 2); };
return permute<N, f>(x, y);
}
But that only works for N=16:
// works
typedef uint8_t test16_t __attribute__((ext_vector_type(16)));
test16_t zip_lo16(test16_t x, test16_t y) {
return zip_lo(x, y);
}
// does not work
typedef uint8_t test8_t __attribute__((ext_vector_type(8)));
test8_t zip_lo8(test8_t& x, test8_t& y) {
return zip_lo(x, y);
}
std::apply
seemed like it might be relevant, but __builtin_shufflevector
refused be passed into that. Even if it worked I still wouldn't know how best to generate the tuple.
You should be able to do with with an index_sequence
:
#include <utility>
template <int (*f)(int), std::size_t N>
byte_vector_t<N> permute(byte_vector_t<N> x, byte_vector_t<N> y) {
return [&]<std::size_t... I>(std::index_sequence<I...>) {
return __builtin_shufflevector(x, y, f(I)...);
}(std::make_index_sequence<N>());
}
template <std::size_t N>
byte_vector_t<N> zip_lo(byte_vector_t<N> x, byte_vector_t<N> y) {
auto f = [](int i) -> int { return ((i & 1) ? N : 0) + (i / 2); };
return permute<f>(x, y);
}
If you use C++17, you can't use generic lambdas so you'd need a helper function:
template <int (*f)(int), std::size_t... I>
byte_vector_t<sizeof...(I)> permute_helper(byte_vector_t<sizeof...(I)> x,
byte_vector_t<sizeof...(I)> y,
std::index_sequence<I...>) {
return __builtin_shufflevector(x, y, f(I)...);
}
template <std::size_t N, int (*f)(int)>
byte_vector_t<N> permute(byte_vector_t<N> x, byte_vector_t<N> y) {
return permute_helper<f>(x, y, std::make_index_sequence<N>());
}
Note: You may want to consider taking the byte_vector
s by reference instead.