Сообщение 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>Типы заранее я не знаю, их будут определять пользователи фреймворка.
Ну, как-то так. Для теcта заюзал fold expressions, поэтому C++17. Но принципиально не вижу преград для понижения до C++14
Идея такая: сначала, основываясь на типе входного тупла и списка типов-фильтров, выводим индексы элементов входного тупла, которые попадают в конечный тупл (метафункция make_filtered_index_sequence), а также тип конечного тупла (filtered_tuple_t) — все это в компайл-тайме, разумеется. После чего одним простым выражением конструируем конечный отфильтрованный тупл (make_filtered_tuple).
Имплементация, конечно, несколько громоздкая, зато использование — проще некуда, по-моему.
http://coliru.stacked-crooked.com/a/14cec1107571c9a1
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,
}
Re: Создать новый std::tuple из подмножества имеющегося
Здравствуйте, SaZ, Вы писали:
SaZ>Всем добра,
SaZ>Ломаю голову над следующей задачей. Дан некий тупл, нужно получить новый, в котором будут только определённые типы (например, унаследованные от некоего my_base класса).
SaZ>Как это сделать?
SaZ>Тупл отдаётся сторонней библиотекой. Хочу написать враппер, который возвращает новый. К примеру, из исходного тупла нужно получить новый в котором будут только строки и целые числа:
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
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,
}