C++-一个参数包可以在一个表达式中扩展多次吗?

xoefb8l8  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(86)

我有一个函数,它需要3个模板参数:两个类型和一个整数常量(用于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上的多个模板“三元组”,我想这可能是template parameter packs的一个很好的用法。
我想实现以下目标:

// 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 */
}


虽然我肯定可以生成一些处理case(1)的代码,但我不知道如何编写处理case(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);
}


参数包应该以这种方式使用吗?我能找到的使用它们的例子只有:

  • 使用模板化的函数参数,
  • 一次使用一个包扩展“用法”(被调用的函数只需要一个类型和一个可变模板包)
ogsagwnx

ogsagwnx1#

你还不能在一个包中捕获混合类型(MatVertexTypeMatIndexType)和非类型(MatOptions)的模板参数,因为从C23开始,一个包必须是单一类型的:类型(typename/class),template,或值(具体或auto)。
有几个可能的解决方案:
1.将非类型模板参数提升到类型空间,例如使用std::integral_constant<Eigen::StorageOption>,以便它可以与类型参数一起传递到类型包中;
1.合并将每个三元组的相关参数组合成一个类型,例如template<class MatVertexType, class MatIndexType, Eigen::StorageOption MatOptions> struct MatParameters;(这是AOS选项);
1.(如您的解决方案https://stackoverflow.com/a/77488183/567292)传递三个同构包,例如使用std::tuple作为类型包,std::integer_sequence(在类型空间中)或std::array(在值空间中)作为值包(这是选项2的SOA变体);
1.等待“Universal Template Parameters“,它已经被提议用于C
26。

puruo6ea

puruo6ea2#

不知道我是否理解了这个问题,但是如果你想在给定Ts...的情况下示例化一些foo<Ts...,Ts...,Ts...>,你可以 Package 函数模板,只需要示例化它:

template <typename ...Ts>
void foo() {};

template <typename ...Ts>
void foo_expand() {
    foo<Ts...,Ts...,Ts...>();
};

字符串

brvekthn

brvekthn3#

为了让其他人也这么做,我最后做了以下事情。
我将模板“triplets”的每个参数打包到类似元组的空结构中,并使用递归调用元组执行打包扩展。下面是结果代码的粗略框架:

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. */

相关问题