LINUX.ORG.RU

std::apply и std::integer_sequence

 ,


0

3

Захотелось странного (ну, почти): заставить работать std::apply с обьектом std::integer_sequence. Ведь последний – вполне себе кортеж.

А целью было бы упрощение паттерна (хз как называется) с обходом кортежей (С++17*):

template <typename Function, 
          typename Tuple, 
          std::size_t... indexes>
void doFoonktion(Function f, Tuple t, std::index_sequence<indexes...>) {
  (static_cast<void>(f(std::get<indexes>(t))), ...);
}

template <typename Function,
          typename Tuple>
void doFunction(Function f, Tuple t) {
  doFoonktion(f, t, std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}

До такого:

template <typename Function,
          typename Tuple>
void doFunction(Function f, Tuple t) {
  std::apply([](auto... index) {
               (static_cast<void>(f(std::get<index>(t))), ...);
             },
             std::make_index_sequence<std::tuple_size_v<T>>{});
}

  • да, я знаю, что в С++20 можно явно делать шаблонные лямбды и второй «внешней» функции уже не надо было бы.

Значит сделать tuple_size и tuple_element для integer_sequence очень даже просто:

template <auto v>
using constant = std::integral_constant<decltype(v), v>;

template <typename T, T... values>
tuple_size<integer_sequence<T, values...>> : constant<sizeof...(values)>
{};

template <size_t i, typename T, T... values>
tuple_element<i, integral_sequence<T, values...>> { using type = T; };

Докостыливаем std::get:

template <std::size_t i, typename T, T first, T... rest>
T get(integer_sequence<T, first, rest...>) {
  if constexpr (i == 0)
    return first;
  else
    return get<i - 1>(integer_sequence<T, rest...>{});
}

template <std::size_t i, typename T>
T get(integer_sequence<T>) {
  static_assert(i != i, "invalid index");
}

После такого финта ушами начинает работать structured binding:

  auto [zero, one, two, three] = std::make_index_sequence<4>{};

Однако std::apply «не видит» нашего std::get, если заголовок <tuple> был включен ДО нашего определения get. Наверное там нету ADL: https://wandbox.org/permlink/GRDV7VEvalUWATlM

Есть идеи как сделать такое красиво/правильно? Ну и еще было бы круто вообще заставить std::apply работать с пользовательскими типами. Пока что на ум приходит только написание собственную реализацию std::apply c шлюхами и ADL.

★★★★★

Последнее исправление: cetjs2 (всего исправлений: 1)

template <std::size_t i, typename T>
T get(integer_sequence<T>) {
  static_assert(i != i, "invalid index");
}

Разве это не ill-formed, NDR?

anonymous
()
Ответ на: комментарий от anonymous

Где именно ill-formed?

inb4: integer_sequence может быть пустым:

The program is ill-formed if N is negative. If N is zero, the indicated type is integer_sequence<T>.

UPD или речь о том, что не надо обрабатывать такой случай?

KennyMinigun ★★★★★
() автор топика
Последнее исправление: KennyMinigun (всего исправлений: 2)

правильно

Кажется, тогда придётся это всё выкинуть:

[namespace.std]

The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

придётся это всё выкинуть

Ну или пинать коммитет :)

Тогда формально остается вторая часть вопроса: std::apply и пользовательские типы

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

Ну или пинать коммитет :)

Они не пойдут на такое, так как это не даст им возможность добавлять специализации на свои же типы в будущем не ломая код. Оно для этого и «undefined».

Тогда формально остается вторая часть вопроса: std::apply и пользовательские типы

Специализация std::apply, вероятно, разрешена. Её и надо писать (в соответствии с требованиями стандарта), так как никакого «интерфейса для расширения» там нет.

xaizek ★★★★★
()

В жизни можно смотреть на три вещи…

Последняя - как плюсовики приседают с функциональным программированием. Сомневаюсь теперь что именно хаскелистов нужно называть штангистами

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 2)
Ответ на: комментарий от KennyMinigun

Где именно ill-formed?

temp.res/8

The program is ill-formed, no diagnostic required, if:
— no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated

anonymous
()
Ответ на: комментарий от xaizek

Они не пойдут на такое

Я скорее думал о добавлении специализацию std::apply (и другого boilerplate) для integer_sequence в стандартную библиотеку.

Её и надо писать, так как никакого «интерфейса для расширения» там нет.

Ух ( А хватило бы только сделать по аналогии со structured bindings.

KennyMinigun ★★★★★
() автор топика
Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от vertexua

Последняя - как плюсовики приседают с функциональным программированием.

Ото не функциональщина даже (только косвенно). Тут вопрос интроспекции и кодогенерации.

KennyMinigun ★★★★★
() автор топика

А целью было бы упрощение паттерна (хз как называется) с обходом кортежей (С++17*):

Ты явно чем-то упоролся.

template <typename Function,
          typename Tuple>
void doFunction(Function f, Tuple t) {
  std::apply([](auto... index) {
               (static_cast<void>(f(std::get<index>(t))), ...);
             },
             std::make_index_sequence<std::tuple_size_v<T>>{});
}

Ты хочешь родить форич для тапла? Ну дак используй нормальный инструментарий - ту же hana, либо напиши форич руками. Зачем ты пытаешься это прикрутить к apply ломая его семантику - мне неведомо. И зачем это делать через такое говно - тоже.

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

  std::apply([](auto... args) {
    auto put = [](auto arg) { std::cout << arg; };
    (put(args), ...);
  }, std::tuple{1, 2, 3});

Зачем ты пытаешься сделать тоже самое через жопу? Хочешь хранить индексы в тапле - хранить в тапле инстансы integral_constant.

anonymous
()
Ответ на: комментарий от anonymous

Ты хочешь родить форич для тапла?

Для любого «кортежа», чтоб его элементы можно было использовать в fold-выражениях.

std::tuple{1, 2, 3}

Вопрос как раз о том, чтоб вместо std::tuple (std::pair, std::array) там был integer_sequence.

Хочешь хранить индексы в тапле - хранить в тапле инстансы integral_constant.

А это интересная идея, спасибо.

P.S. хамить не обязательно.

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

Для любого «кортежа», чтоб его элементы можно было использовать в fold-выражениях.

Итак можно использовать, принимай как std::tuple<Ts...>

Вопрос как раз о том, чтоб вместо std::tuple (std::pair, std::array) там был integer_sequence.

Зачем? То, где ты это показал - это не имеет смысла. В общем случае смысл этого так же очень сомнительный.

А это интересная идея, спасибо.

Это базовые идеи. Как я уже говорил - хочешь что-то нормальное - делай сам, либо как минимум используй хану. В stdlib мало что есть.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.