Re: Создать новый std::tuple из подмножества имеющегося
От: rg45 СССР  
Дата: 22.02.23 02:16
Оценка: 6 (1) +1
Здравствуйте, SaZ, Вы писали:

SaZ>Всем добра,

SaZ>Ломаю голову над следующей задачей. Дан некий тупл, нужно получить новый, в котором будут только определённые типы (например, унаследованные от некоего my_base класса).
SaZ>Как это сделать?
SaZ>Тупл отдаётся сторонней библиотекой. Хочу написать враппер, который возвращает новый. К примеру, из исходного тупла нужно получить новый в котором будут только строки и целые числа:
SaZ>
SaZ>std::tuple<int, string, bool, string, float> -> как получить? -> std::tuple<int, string, string>;
SaZ>

SaZ>Типы заранее я не знаю, их будут определять пользователи фреймворка.

Ну, как-то так. Для теcта заюзал fold expressions, поэтому C++17. Но принципиально не вижу преград для понижения до C++14

Идея такая: сначала, основываясь на типе входного тупла и списке типов-фильтров, выводим список индексов элементов входного тупла, которые попадают в конечный тупл после фильтрации (метафункция make_filtered_index_sequence), а также тип конечного тупла (filtered_tuple_t) — все это в компайл-тайме, разумеется. После чего одним простым выражением конструируем конечный отфильтрованный тупл (make_filtered_tuple).

Имплементация, конечно, несколько громоздкая, зато использование — проще некуда, по-моему.

   const auto tuple = std::make_tuple("Morning", 42, std::string("Hello"), 3.14, true, std::string("World"), 'A');

   print(make_filtered_tuple<int, std::string>(tuple)); // -> 42, Hello, World, 
   print(make_filtered_tuple<double, const char*, char>(tuple)); // -> Morning, 3.14, A,


http://coliru.stacked-crooked.com/a/14cec1107571c9a1

  Полный текст примера
#include <iostream>
#include <string>
#include <tuple>
#include <utility>

//////////////////////////////////////////////
// Определяем, находится ли тип в списке типов

template <typename T, typename...>
struct is_in_the_list : std::false_type {};

template <typename T, typename U, typename...X>
struct is_in_the_list<T, U, X...> : is_in_the_list<T, X...> {};

template <typename T, typename...X>
struct is_in_the_list<T, T, X...> : std::true_type {};

//////////////////////////////////////////////////////////////////////////////
// Строим список индексов входного тупла, которые попадают в конечный тупл

template <typename Tuple, typename FilterTuple, typename I = std::index_sequence<0>, typename = void>
struct filtered_index_sequence;

template <typename T, typename...S, typename...F, size_t I, size_t...J>
struct filtered_index_sequence<std::tuple<T, S...>, std::tuple<F...>, std::index_sequence<I, J...>,
   std::enable_if_t<is_in_the_list<T, F...>::value>> :
   filtered_index_sequence<std::tuple<S...>, std::tuple<F...>, std::index_sequence<I + 1, J..., I>> { };

template <typename T, typename...S, typename...F, size_t I, size_t...J>
struct filtered_index_sequence<std::tuple<T, S...>, std::tuple<F...>, std::index_sequence<I, J...>,
   std::enable_if_t<!is_in_the_list<T, F...>::value>> :
   filtered_index_sequence<std::tuple<S...>, std::tuple<F...>, std::index_sequence<I + 1, J...>> { };

template <typename...F, size_t I, size_t...J>
struct filtered_index_sequence<std::tuple<>, std::tuple<F...>, std::index_sequence<I, J...>>
{
   using type = std::index_sequence<J...>;
};

template <typename Tuple, typename...Filter>
using make_filtered_index_sequence = typename filtered_index_sequence<Tuple, std::tuple<Filter...>>::type;

///////////////////////////////
// Выводим тип конечного тупла

template <typename Tuple, typename...Filter>
struct filtered_tuple;

template <typename...T, size_t...I>
struct filtered_tuple<std::tuple<T...>, std::index_sequence<I...>> {
   using type = std::tuple<std::tuple_element_t<I, std::tuple<T...>>...>;
};

template <typename...T, typename...Filter>
struct filtered_tuple<std::tuple<T...>, Filter...> :
filtered_tuple<std::tuple<T...>, make_filtered_index_sequence<std::tuple<T...>, Filter...>> {};

template <typename Tuple, typename...Filter>
using filtered_tuple_t = typename filtered_tuple<Tuple, Filter...>::type;

///////////////////////////////////////
// Фильтрация тупла по списку индексов

template <typename...T, size_t...I>
auto make_filtered_tuple(const std::tuple<T...>& tuple, std::index_sequence<I...>)
   -> filtered_tuple_t<std::tuple<T...>, std::index_sequence<I...>>
{
   return { std::get<I>(tuple)... };
}

////////////////////////////////////
// Фильтрация тупла по списку типов

template <typename...Filter, typename...T>
auto make_filtered_tuple(const std::tuple<T...>& tuple)
    -> filtered_tuple_t<std::tuple<T...>, Filter...>
{
   return make_filtered_tuple(tuple, make_filtered_index_sequence<std::tuple<T...>, Filter...>{});
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Try to use

template <typename...T, size_t...I>
void print(const std::tuple<T...>& tuple, std::index_sequence<I...>)
{
   ((std::cout << std::get<I>(tuple) << ", "), ...) << std::endl;
}

template <typename...T>
void print(const std::tuple<T...>& tuple)
{
   print(tuple, std::index_sequence_for<T...>{});
}

int main()
{
   const auto tuple = std::make_tuple("Morning", 42, std::string("Hello"), 3.14, true, std::string("World"), 'A');

   print(make_filtered_tuple<int, std::string>(tuple)); // -> 42, Hello, World, 
   print(make_filtered_tuple<double, const char*, char>(tuple)); // -> Morning, 3.14, A, 
}


Если вывод типа конечного тупла в явном виде не нужен (filtered_tuple_t), можно без него.

http://coliru.stacked-crooked.com/a/f1cb953f87844449

  То же самое, но без вывода типа конечного тупла (чуть компактнее)
#include <iostream>
#include <string>
#include <tuple>
#include <utility>

template <typename T, typename...>
struct is_in_the_list : std::false_type {};

template <typename T, typename U, typename...X>
struct is_in_the_list<T, U, X...> : is_in_the_list<T, X...> {};

template <typename T, typename...X>
struct is_in_the_list<T, T, X...> : std::true_type {};

template <typename Tuple, typename FilterTuple, typename I = std::index_sequence<0>, typename = void>
struct filtered_index_sequence;

template <typename T, typename...S, typename...F, size_t I, size_t...J>
struct filtered_index_sequence<std::tuple<T, S...>, std::tuple<F...>, std::index_sequence<I, J...>,
   std::enable_if_t<is_in_the_list<T, F...>::value>> :
   filtered_index_sequence<std::tuple<S...>, std::tuple<F...>, std::index_sequence<I + 1, J..., I>> { };

template <typename T, typename...S, typename...F, size_t I, size_t...J>
struct filtered_index_sequence<std::tuple<T, S...>, std::tuple<F...>, std::index_sequence<I, J...>,
   std::enable_if_t<!is_in_the_list<T, F...>::value>> :
   filtered_index_sequence<std::tuple<S...>, std::tuple<F...>, std::index_sequence<I + 1, J...>> { };

template <typename...F, size_t I, size_t...J>
struct filtered_index_sequence<std::tuple<>, std::tuple<F...>, std::index_sequence<I, J...>>
{
   using type = std::index_sequence<J...>;
};

template <typename Tuple, typename...Filter>
using make_filtered_index_sequence = typename filtered_index_sequence<Tuple, std::tuple<Filter...>>::type;

template <typename...T, size_t...I>
auto make_filtered_tuple(const std::tuple<T...>& tuple, std::index_sequence<I...>)
{
   return std::make_tuple(std::get<I>(tuple)...);
}

template <typename...Filter, typename...T>
auto make_filtered_tuple(const std::tuple<T...>& tuple)
{
   return make_filtered_tuple(tuple, make_filtered_index_sequence<std::tuple<T...>, Filter...>{});
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Try to use

template <typename...T, size_t...I>
void print(const std::tuple<T...>& tuple, std::index_sequence<I...>)
{
   ((std::cout << std::get<I>(tuple) << ", "), ...) << std::endl;
}

template <typename...T>
void print(const std::tuple<T...>& tuple)
{
   print(tuple, std::index_sequence_for<T...>{});
}

int main()
{
   const auto tuple = std::make_tuple("Morning", 42, std::string("Hello"), 3.14, true, std::string("World"), 'A');

   print(make_filtered_tuple<int, std::string>(tuple)); // -> 42, Hello, World, 
   print(make_filtered_tuple<double, const char*, char>(tuple)); // -> Morning, 3.14, A, 
}
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 22.02.2023 13:44 rg45 . Предыдущая версия . Еще …
Отредактировано 22.02.2023 12:03 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 11:00 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 10:46 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 10:45 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 10:42 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 10:19 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 3:47 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 3:47 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 3:28 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 2:44 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 2:41 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 2:21 rg45 . Предыдущая версия .
Отредактировано 22.02.2023 2:17 rg45 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.