Обработка NULL при использовании std :: move для кортежей - PullRequest
0 голосов
/ 15 октября 2019

Я работаю с приведенным ниже примером кода.

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 присутствует в качестве одного из аргументов в фактических значениях, код выдает ошибку, как показано ниже (тип 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 и фактических данных.

Ответы [ 2 ]

1 голос
/ 15 октября 2019

Не проблема std::move.

Проблема в том, что C ++ является статическим типизированным языком. Поэтому, если у вас есть присваивание типа

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

, где тип кортежа decltype(actuals) равен 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

Если вы действительно хотите / нуждаетесь в использовании 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));

- РЕДАКТИРОВАТЬ -

ОП запрашивает

Не могли бы вы рассказать подробнее об используемой index_sequence. Кроме того, действительно ли нам нужна вспомогательная функция или это можно сделать без этого

Индексы std::index_sequence необходимы для извлечения значений из кортежа act;к сожалению, пара std::index_sequence / std::make_index_sequence представлена ​​в C ++ 14, но, если вам это нужно в C ++ 11, существует множество альтернативных реализаций C ++ 11.

К сожалению,последовательность индексов в std::index_sequence требует создания std::make_index_sequence из sizeof...(), поэтому необходим вызов функции, поэтому необходима функция foo_helper().

Исключение.

Если ваш act кортеж как точно один элемент для каждого типа, вы можете (начиная с C ++ 14) просто написать

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

непосредственно внутри foo(), не определяя /вызов foo_helper().

Но предложение о запрещении столкновений - большой предел, ИМХО.

0 голосов
/ 16 октября 2019

Вот как выглядит мой модифицированный код, который, кажется, работает. Но пришлось добавить так много кода только для обработки пустого случая.

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));
.
.
.
}
...