C++参数包能否单次表达式多次展开?多模板三元组绑定Python求解
问题:用模板参数包批量绑定多组Eigen模板函数到Python
我有一个带三个模板参数的函数my_big_function,包含两种类型和一个Eigen存储选项枚举:
template <typename VertexType, typename IndexType, Eigen::StorageOptions Options> void my_big_function(/* 3 Eigen parameters depending on template args... */) { /* Do stuff with matrices... */ }
我需要把多组模板参数"三元组"绑定到Python,觉得模板参数包是合适的方案,期望实现:
// 绑定单个版本:my_big_function<float, int, ColMajor>(...) bind_my_big_function<float, int, Eigen::ColMajor>(m); // (1) // 绑定三个版本: // - my_big_function<float, int, ColMajor>(...) // - my_big_function<float, int, RowMajor>(...) // - my_big_function<double, int, RowMajor>(...) bind_my_big_function<float, int, Eigen::ColMajor, float, int, Eigen::RowMajor, double, int, Eigen::RowMajor>(m); // (2)
预期的bind_my_big_function定义大概是这样:
template <typename MatVertexType, typename MatIndexType, Eigen::StorageOptions MatOptions, typename...RemainingArgs> void bind_my_big_function(pybind11::module_& m) { /* 绑定当前三元组的函数,然后递归绑定剩余参数 */ }
目前我只能处理场景(1),场景(2)无法正常工作,尝试的代码如下:
template <typename MatVertexType, typename MatIndexType, Eigen::StorageOptions MatOptions> void bind_my_big_function(pybind11::module_& m) { m.def("my_big_function_in_python", &my_big_function<MatVertexType, MatIndexType, MatOptions>, /* python arguments...*/); } template < typename MatVertexType, typename MatIndexType, Eigen::StorageOptions MatOptions, typename...RemainingArgs, typename = typename std::enable_if_t<sizeof...(RemainingArgs) != 0> > void bind_my_big_function(pybind11::module_& m) { m.def("my_big_function_in_python", &my_big_function<MatVertexType, MatIndexType, MatOptions>, /* python arguments...*/); bind_my_big_function<RemainingArgs...>(m); }
请问参数包允许这样的用法吗?我找到的示例只涉及模板函数参数、单次展开参数包的场景。
解答
模板参数包完全支持这种递归分组展开的用法,你当前的代码问题在于递归调用时的重载匹配逻辑错误:第二个重载的enable_if写法会导致递归到剩余参数不足时,无法正确匹配终止版本,同时参数包的分组传递逻辑也有瑕疵。
正确的实现方式是拆分出终止递归的基础版本和处理三元组的递归版本:
// 终止递归的基础版本:无参数时直接返回 void bind_my_big_function(pybind11::module_& m) {} // 递归版本:每次处理一组(类型、类型、枚举)三元组,再递归处理剩余参数 template <typename MatVertexType, typename MatIndexType, Eigen::StorageOptions MatOptions, typename... RemainingArgs> void bind_my_big_function(pybind11::module_& m) { // 绑定当前模板参数对应的函数 m.def("my_big_function_in_python", &my_big_function<MatVertexType, MatIndexType, MatOptions>, /* python 参数说明... */); // 递归处理剩余的参数包 bind_my_big_function<RemainingArgs...>(m); }
调用逻辑说明
- 场景(1):调用
bind_my_big_function<float, int, Eigen::ColMajor>(m)会匹配递归版本,绑定函数后递归调用空参数版本,触发终止逻辑。 - 场景(2):依次处理三组三元组,每次绑定后递归传递剩余参数,直到最后触发空参数的终止版本。
额外安全优化
为了避免传入参数数量不是3的倍数导致编译错误,可以添加静态断言做前置检查:
template <typename... Args> constexpr bool is_multiple_of_three() { return sizeof...(Args) % 3 == 0; } template <typename MatVertexType, typename MatIndexType, Eigen::StorageOptions MatOptions, typename... RemainingArgs> void bind_my_big_function(pybind11::module_& m) { static_assert(is_multiple_of_three<MatVertexType, MatIndexType, decltype(MatOptions), RemainingArgs...>(), "Template arguments must be groups of (VertexType, IndexType, StorageOptions)"); m.def("my_big_function_in_python", &my_big_function<MatVertexType, MatIndexType, MatOptions>, /* python 参数... */); bind_my_big_function<RemainingArgs...>(m); }
这样就能确保传入的参数都是合法的三元组分组,提前暴露错误。
内容的提问来源于stack exchange,提问作者Thibault de Villèle
相关产品推荐
相关产品推荐

