在元组上使用 std::move 时处理 NULL

Handling NULL while using std::move on the tuples

我正在使用以下示例代码。

auto kernarg = hip_impl::make_kernarg(kernel, std::tuple<Args...>{std::move(args)...});
.
.
.

template <typename... Formals, typename... Actuals>
inline impl::kernarg make_kernarg(
void (*kern)(Formals...), std::tuple<Actuals...> actuals) {
static_assert(sizeof...(Formals) == sizeof...(Actuals),
    "The count of formal arguments must match the count of actuals.");

if (sizeof...(Formals) == 0) return {};

std::tuple<Formals...> to_formals{std::move(actuals)};
impl::kernarg kernarg;
kernarg.reserve(sizeof(to_formals));
.
.
.

当 NULL 作为 actuals 中的参数之一出现时,代码给出如下错误(在这种情况下 NULL 的类型是 long int)。

grid_launch.hpp:96:28: error: no matching constructor for initialization of 'std::tuple<float *, int, int *>'
std::tuple<Formals...> to_formals{std::move(actuals)};
                       ^         ~~~~~~~~~~~~~~~~~~~~
grid_launch.hpp:165:30: 
note: in instantiation of function template specialization 
'impl::make_kernarg<float *, int, int *, float *, int, long>' 
requested here
auto kernarg = impl::make_kernarg(kernel, std::tuple<Args...> 
{std::move(args)...});

可以看出,这里期望的参数类型是int*,但是NULL是long类型,所以报错。

在这种情况下,我们如何在元组实际值中找到哪个参数为 NULL 并获取它并进行类型转换或做一些事情以将其与元组中的确切类型匹配 to_formals(这里是 int*,但它可以是任何指向整数类型的指针,具体取决于情况)。我们需要从 to_formals 和 actuals 中获取所有值吗?

不是std::move的问题。

问题在于 C++ 是静态类型语言。所以如果你有一个类型

的赋值
std::tuple<Formals...> to_formals{std::move(actuals)};

其中 decltype(actuals) 元组的类型是 long,编译器无法评估,编译时,如果 long 值为零,则将其转换为相同其他类型的指针, 与否。

针对此类问题,C++11 引入了空指针类型 (std::nullptr_t) 和一个新关键字来避免 NULL (nullptr)。

因此,举例来说,如果您有如下函数

template <typename ... Formals, typename ... Actuals>
void foo (void(*)(Formals...), std::tuple<Actuals...> act)
 {
   std::tuple<Formals...> frm { std::move(act) };
 }

并给定一个函数void(float *, int, int *)

void bar (float *, int, int *)
 { }

NULL

通话
foo(&bar, std::make_tuple(&f0, 0, NULL)); // compilaton error

你得到一个编译错误(我想你的问题是一样的)但是使用 nullptr

foo(&bar, std::make_tuple(&f0, 0, nullptr)); // compile

如果你真的 want/need 使用 NULL 而不是 nullptr,我能想象的最好的办法是将 actuals 元组的每个元素都转换为正式的类型元组;如下

template <typename ... Formals, typename ... Actuals, std::size_t ... Is>
void foo_helper (void(*)(Formals...), std::tuple<Actuals...> act,
                 std::index_sequence<Is...>)
 {
   std::tuple<Formals...> frm { (Formals)std::get<Is>(act)... };
 }

template <typename ... Formals, typename ... Actuals>
void foo (void(*pfunc)(Formals...), std::tuple<Actuals...> act)
 {
   foo_helper(pfunc, act, std::make_index_sequence<sizeof...(Actuals)>{});
 }

现在也编译

foo(&bar, std::make_tuple(&f0, 0, NULL));

-- 编辑--

OP 询问

can you please elaborate on the index_sequence being used. Also, do we really need a helper function or it can be done without that

需要 std::index_sequence 的索引来从 act 元组中提取值;不幸的是,std::index_sequence/std::make_index_sequence 这对夫妇在 C++14 中引入,但是,如果您在 C++11 中需要它,则有很多 C++11 替代实现。

不幸的是,std::index_sequence中的索引序列需要从sizeof...()中创建一个std::make_index_sequence,所以需要一个函数调用,所以foo_helper()中的一个函数需要。

异常。

如果您的 act 元组 恰好 每个类型一个元素,您可以(从 C++14 开始)简单地写

std::tuple<Formals...> frm { (Formals)std::get<Actuals>(act)... };

直接在foo()里面,没有defining/calling一个foo_helper().

但是无类型冲突条款是一个很大的限制,恕我直言。

这是我修改后的代码的样子,似乎可以正常工作。但是不得不添加这么多代码来处理 NULL 情况。

auto kernarg = hip_impl::make_kernarg(kernel, std::tuple<Args...>{std::move(args)...});
.
.
.     
template <std::size_t ...>
struct indexSequence
 { };

template <std::size_t N, std::size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
 { };

template <std::size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
 { using type = indexSequence<Next ... >; };

template <std::size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;

template <typename ... Formals, typename ... Actuals, std::size_t ... Is>
std::tuple <Formals...> helper (void(*kernel)(Formals...), std::tuple<Actuals...> act,
                 indexSequence<Is...>){
        std::tuple<Formals...> to_formals{ (Formals)std::get<Is>(act)...};
        return to_formals;
}


template <typename... Formals, typename... Actuals>
hip_impl::kernarg make_kernarg(
void (*kernel)(Formals...), std::tuple<Actuals...> actuals) {
static_assert(sizeof...(Formals) == sizeof...(Actuals),
    "The count of formal arguments must match the count of actuals.");

if (sizeof...(Formals) == 0) return {};

std::tuple<Formals...> to_formals;
to_formals = helper(kernel, actuals, makeIndexSequence<sizeof...(Actuals)>{});

hip_impl::kernarg kernarg;
kernarg.reserve(sizeof(to_formals));
.
.
.
}