如何按模板参数指定长度生成函数参数序列并泛化模板
问题:泛化向量洗牌函数以支持任意长度的模板参数
现有代码利用GCC的__builtin_shufflevector实现向量元素重排,但仅支持固定长度N=16的向量。需要实现泛化方案,让函数根据模板参数N自动生成对应数量的f(i)参数序列传递给__builtin_shufflevector,适配任意指定长度的向量。
原代码如下:
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); }
上述代码仅在N=16时生效,N=8等其他值会编译失败——因为__builtin_shufflevector需要恰好N个索引参数,而原代码硬编码了16个。
解决方案1:基于C++11整数序列的泛化实现
核心思路是用std::integer_sequence生成编译期整数序列,展开为__builtin_shufflevector的参数列表;同时替换lambda为constexpr函数,解决非类型模板参数的兼容性问题。
1. 实现参数展开的辅助模板
#include <utility> // 用于std::integer_sequence // 辅助函数:通过整数序列展开索引参数 template<std::size_t N, int(*f)(int), std::size_t... Is> byte_vector_t<N> permute_impl(byte_vector_t<N> x, byte_vector_t<N> y, std::index_sequence<Is...>) { return __builtin_shufflevector(x, y, f(Is)...); }
2. 封装对外调用接口
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_impl<N, f>(x, y, std::make_index_sequence<N>{}); }
3. 替换lambda为constexpr函数
由于C++20前lambda无法作为非类型模板参数,将索引计算逻辑封装为constexpr函数:
template<std::size_t N> constexpr int zip_lo_idx(int i) { return ((i & 1)? static_cast<int>(N) : 0) + (i / 2); } template<std::size_t N> byte_vector_t<N> zip_lo(byte_vector_t<N> x, byte_vector_t<N> y) { return permute<N, zip_lo_idx<N>>(x, y); }
测试验证
// N=16 正常工作 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); } // N=8 现在可正常编译运行 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); }
解决方案2:C++20模板lambda简化实现
如果使用C++20及以上版本,可直接用模板lambda作为非类型模板参数,省去单独定义constexpr函数的步骤:
#include <utility> template<std::size_t N, auto F> byte_vector_t<N> permute(byte_vector_t<N> x, byte_vector_t<N> y) { auto impl = []<std::size_t... Is>(auto x, auto y, std::index_sequence<Is...>) { return __builtin_shufflevector(x, y, F(Is)...); }; return impl(x, y, 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) { return permute<N, [](int i) constexpr { return ((i & 1)? static_cast<int>(N) : 0) + (i / 2); }>(x, y); }
关键说明
std::index_sequence负责生成0到N-1的编译期整数序列,确保__builtin_shufflevector的索引参数数量与向量长度严格匹配。- GCC的
__builtin_shufflevector对参数数量有严格要求,模板展开机制完美适配任意N值的向量。 - C20前后的差异主要体现在非类型模板参数的支持范围:C20前需用
constexpr函数替代lambda,C++20及以上可直接使用模板lambda简化代码。
内容的提问来源于stack exchange,提问作者sh1
相关产品推荐
相关产品推荐

