Информация об изменениях

Сообщение Re: Создать новый std::tuple из подмножества имеющегося от 22.02.2023 2:16

Изменено 22.02.2023 3:47 rg45

Re: Создать новый std::tuple из подмножества имеющегося
Здравствуйте, 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

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

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, 
}
Re: Создать новый std::tuple из подмножества имеющегося
Здравствуйте, 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).

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

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, 
}