提问人:Thibault de Villèle 提问时间:11/15/2023 最后编辑:Thibault de Villèle 更新时间:11/16/2023 访问量:93
C++ − 单个参数包是否可以在单个表达式中多次扩展?
C++ − Can a single parameter pack be expanded more than once in a single expression?
问:
我有一个函数,它采用 3 个模板参数:两种类型和一个整数常量(用于特征存储要求)。它看起来像以下模板:
template <typename VertexType, typename IndexType, Eigen::StorageOptions Options>
void my_big_function(/* 3 Eigen parameters depending on template args... */) {
/* Do stuff with matrices... */
}
而且由于这个函数需要绑定到具有多个模板“三元组”的 python,我认为这可能是模板参数包的一个很好的用法。
我想实现以下目标:
// binds to python :
// - my_big_function<float, int, ColMajor>(...)
bind_my_big_function<float, int, Eigen::ColMajor>(m); // (1)
// binds to python :
// - 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) {
/* bind function taking MatVertexType, MatIndexType & MatOptions and go onto binding other variants of my_big_function */
}
虽然我肯定可以生成一些处理大小写 (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);
}
参数包应该以这种方式使用吗?我能找到的唯一使用它们的例子是:
- 使用模板化函数参数,
- 一次使用单个包扩展(调用函数只需要一种类型和一个可变参数模板包)
答:
0赞
463035818_is_not_an_ai
11/15/2023
#1
不确定我是否理解这个问题,但如果你想实例化一些给定的东西,你可以包装函数模板,只需要实例化它:foo<Ts...,Ts...,Ts...>
Ts...
template <typename ...Ts>
void foo() {};
template <typename ...Ts>
void foo_expand() {
foo<Ts...,Ts...,Ts...>();
};
评论
0赞
Thibault de Villèle
11/15/2023
问题似乎源于我混合了类型名和定义良好的类/类型:编译器在我的 OP 中的第二个用例中抛出错误,因为枚举与类型名匹配。似乎参数包实际上不是一次扩展 3 个项目,而是一次扩展一个项目。Eigen::StorageOption
0赞
Thibault de Villèle
11/15/2023
#2
为了让其他人尝试做同样的事情,这就是我最终所做的。
我将模板“三元组”的每个参数打包到类似元组的空结构中,并使用对元组的递归调用执行包扩展。下面是结果代码的粗略骨架:
template <typename... Types> struct type_tuple {};
template <Eigen::StorageOptions...> struct eigen_storage_tuple {};
template <typename VertexType, typename IndexType, Eigen::StorageOptions Options>
constexpr void bind(pybind11::module_& m) {
/* Bind the current template of my_big_func() given params above: */
m.def(/* ... */);
}
template <
typename VertexType, template <typename> class VertexTypeTuple,
typename IndexType, template <typename> class IndexTypeTuple,
Eigen::StorageOptions Options, template <Eigen::StorageOptions> class OptionTypeTuple
>
constexpr void bind_recursively(
pybind11::module_& m,
const VertexTypeTuple<VertexType> vertex_types,
const IndexTypeTuple<IndexType> index_types,
const OptionTypeTuple<Options> option_type
) {
bind<VertexType, IndexType, Options>(m);
}
template <
typename VertexType, typename... VertexTypes, template <typename, typename...> class VertexTypeTuple,
typename IndexType, typename... IndexTypes, template <typename, typename...> class IndexTypeTuple,
Eigen::StorageOptions Option, Eigen::StorageOptions... Options, template <Eigen::StorageOptions, Eigen::StorageOptions...> class OptionsTypeTuple
>
constexpr void bind_recursively(
pybind11::module_& m,
const VertexTypeTuple<VertexType, VertexTypes...> vertex_types,
const IndexTypeTuple<IndexType, IndexTypes...> index_types,
const OptionsTypeTuple<Option, Options...> options
) {
VertexTypeTuple<VertexType> current_vertex_type;
IndexTypeTuple<IndexType> current_index_type;
OptionsTypeTuple<Option> current_option;
VertexTypeTuple<VertexTypes...> remaining_vertex_type;
IndexTypeTuple<IndexTypes...> remaining_index_type;
OptionsTypeTuple<Options...> remaining_option;
bind_recursively(m, current_vertex_type, current_index_type, current_option);
bind_recursively(m, remaining_vertex_type, remaining_index_type, remaining_option);
}
函数调用中的所有元组参数仅用于重载解析和包扩展。
由于每个参数包在递归绑定函数中都由“具体”类型包围,因此我能够遍历所有三个包并在编译时生成所有调用。下面是如何调用该函数并解析所有调用的示例:pybind11::module_::def()
constexpr type_tuple<
float, float, float, float, /* For col major types */
float, float, float, float, /* For row major types */
double, double, double, double, /* For col major types */
double, double, double, double /* For row major types */
> remove_unused_vertices_vertex_types;
constexpr type_tuple<
std::int32_t, std::uint32_t, std::int64_t, std::uint64_t, /* For col major types */
std::int32_t, std::uint32_t, std::int64_t, std::uint64_t, /* For row major types */
std::int32_t, std::uint32_t, std::int64_t, std::uint64_t, /* For col major types */
std::int32_t, std::uint32_t, std::int64_t, std::uint64_t /* For row major types */
> remove_unused_vertices_index_types;
constexpr eigen_storage_tuple<
Eigen::StorageOptions::ColMajor, Eigen::StorageOptions::ColMajor, Eigen::StorageOptions::ColMajor, Eigen::StorageOptions::ColMajor,
Eigen::StorageOptions::RowMajor, Eigen::StorageOptions::RowMajor, Eigen::StorageOptions::RowMajor, Eigen::StorageOptions::RowMajor,
Eigen::StorageOptions::ColMajor, Eigen::StorageOptions::ColMajor, Eigen::StorageOptions::ColMajor, Eigen::StorageOptions::ColMajor,
Eigen::StorageOptions::RowMajor, Eigen::StorageOptions::RowMajor, Eigen::StorageOptions::RowMajor, Eigen::StorageOptions::RowMajor
> remove_unused_vertices_storage_orders;
bind_recursively(
cull_module,
remove_unused_vertices_vertex_types,
remove_unused_vertices_index_types,
remove_unused_vertices_storage_orders
);
/* Will bind all combinations of the vertex, index, and storage order types given above. */
1赞
ecatmur
11/15/2023
#3
您还不能在单个包中捕获混合类型 (, ) 和非类型 () 模板参数,因为从 C++23 开始,包必须是单一类型:类型 (/)、或值(具体或 )。MatVertexType
MatIndexType
MatOptions
typename
class
template
auto
有几种可能的解决方案:
- 使用 例如,将非类型模板参数提升为类型空间,以便它可以与类型参数一起传递到类型包中;
std::integral_constant<Eigen::StorageOption>
- 将相关参数的每三元组组合成一种类型,例如 (这是 AOS 选项);
template<class MatVertexType, class MatIndexType, Eigen::StorageOption MatOptions> struct MatParameters;
- (如您的解决方案 https://stackoverflow.com/a/77488183/567292)通过三个均质包装,例如 对于类型包,(在类型空间中)或(在值空间中)对于值包(这是选项 2 的 SOA 变体);
std::tuple
std::integer_sequence
std::array
- 等待“通用模板参数”,这是为 C++26 提出的。
评论
0赞
Thibault de Villèle
11/16/2023
我已经找到了一个解决方案,我将其发布在答案中,这基本上是您提出的 (3.) 的不同版本。我有三个类似元组的结构,由于递归函数调用,我对其进行了迭代。
0赞
Thibault de Villèle
11/16/2023
不过,仍然存在几个问题:如果我使用一个常量并只获取模板包中的类型名称,我仍然无法一次迭代 3 个项目,对吧?如果包从子序列的第二项开始扩展,我是否必须提供部分专用化,或者使用 3 个模板参数和一个参数包提供单个部分专用化就足够了?std::integer_constant
0赞
ecatmur
11/16/2023
@ThibaultdeVillèle是的,要一次使用多个包元素,您必须使用递归或某种等效技术。检查是否递归使用的单个函数模板可能是最干净的选择。if constexpr (sizeof...(Rest) != 0)
评论
Eigen::StorageOptions MatOptions
typename