Search code examples
c++templatesparameter-passingclang++

How to generate function arguments as a sequence with length specified in a template argument


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.


Solution

  • 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);
    }
    

    Demo


    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_vectors by reference instead.