I eventually found a way to write a pack::view structure that should generalize to other views than stride. It makes it possible to write something like:
using foo = pack::view_t <std::views::stride(3),char,int,long,float,double>;
where we explicitly use std::views::stride as a non type template parameter. It is also possible to compose views:
using drop_take_reverse = typename pack::view_t <
std::views::drop(1)
| std::views::take(3)
| std::views::reverse,
char,int,long,float,double
>;
static_assert (std::is_same_v<drop_take_reverse, std::tuple<float,long,int>>);
The idea is to create an constexpr std::array from the view representing the indexes to be kept and then "transform" the content of the array into a parameter pack (see here for a solution) used for filtering the required types.
#include <tuple>
#include <array>
#include <ranges>
//////////////////////////////////////////////////////////////////////////////////////////
// see https://stackoverflow.com/questions/60434033/how-do-i-expand-a-compile-time-stdarray-into-a-parameter-pack
template <auto arr, template <auto...> typename Consumer, typename IS = decltype(std::make_index_sequence<arr.size()>())>
struct Generator;
template <auto arr, template <auto...> typename Consumer, std::size_t... I>
struct Generator<arr, Consumer, std::index_sequence<I...>>
{
using type = Consumer<arr[I]...>;
};
//////////////////////////////////////////////////////////////////////////////////////////
namespace pack
{
template<auto View, typename...ARGS>
struct view
{
// we create a std::array holding the indexes to be kept (no std::ranges::to<std::array<,>> apparently)
constexpr static auto indexes_as_array()
{
// we define the indexes to be kept in the parameters pack.
constexpr auto indexes_view = std::views::iota (size_t{0}, sizeof...(ARGS)) | View ;
std::array<int,indexes_view.size()> arr;
for (auto [i,x] : indexes_view | std::views::enumerate) { arr[i]=x; }
return arr;
}
template <auto... s>
struct ConsumerStruct
{
using type = std::tuple< std::tuple_element_t<s, std::tuple<ARGS...> >...>;
};
// we define our type as an application of the Generator with our ConsumerStruct.
using type = typename Generator<indexes_as_array(), ConsumerStruct>::type::type;
};
template<auto View, typename ...ARGS> using view_t = typename view<View,ARGS...>::type;
}
//////////////////////////////////////////////////////////////////////////////////////////
template<int R>
using stride = typename pack::view_t <std::views::stride(R),char,int,long,float,double>;
static_assert (std::is_same_v< stride<1>, std::tuple<char,int,long,float,double>>);
static_assert (std::is_same_v< stride<2>, std::tuple<char, long, double>>);
static_assert (std::is_same_v< stride<3>, std::tuple<char, float >>);
static_assert (std::is_same_v< stride<4>, std::tuple<char, double>>);
static_assert (std::is_same_v< stride<5>, std::tuple<char >>);
//////////////////////////////////////////////////////////////////////////////////////////
template<int R>
using drop = typename pack::view_t <std::views::drop(R),char,int,long,float,double>;
static_assert (std::is_same_v< drop<0>, std::tuple<char,int,long,float,double>>);
static_assert (std::is_same_v< drop<1>, std::tuple< int,long,float,double>>);
static_assert (std::is_same_v< drop<2>, std::tuple< long,float,double>>);
static_assert (std::is_same_v< drop<3>, std::tuple< float,double>>);
static_assert (std::is_same_v< drop<4>, std::tuple< double>>);
static_assert (std::is_same_v< drop<5>, std::tuple< >>);
//////////////////////////////////////////////////////////////////////////////////////////
template<int R>
using take = typename pack::view_t <std::views::take(R),char,int,long,float,double>;
static_assert (std::is_same_v< take<5>, std::tuple<char,int,long,float,double>>);
static_assert (std::is_same_v< take<4>, std::tuple<char,int,long,float >>);
static_assert (std::is_same_v< take<3>, std::tuple<char,int,long >>);
static_assert (std::is_same_v< take<2>, std::tuple<char,int >>);
static_assert (std::is_same_v< take<1>, std::tuple<char >>);
static_assert (std::is_same_v< take<0>, std::tuple< >>);
//////////////////////////////////////////////////////////////////////////////////////////
template<int A, int B>
using drop_take_reverse = typename pack::view_t <std::views::drop(A) | std::views::take(B) | std::views::reverse, char,int,long,float,double>;
static_assert (std::is_same_v<drop_take_reverse<1,3>, std::tuple<float,long,int>>);
//////////////////////////////////////////////////////////////////////////////////////////
int main() {}
Demo
A limitation is that we need to call size one the view in order to know the size of the array to be created, so views like std::views::filter can't be used in this context.
UPDATE
We can go beyong this limitation with a trick: we compute an array of size sizeof...(ARGS) and fill it with the items of the view. We also remember the actual number of items of the view => it is used later in order to shrink the indexes array to the correct size.
Demo
So we can also write something like:
using filtereven = pack::view_t<std::views::filter([](int n) {return n%2==0;}), char,int,long,float,double>;
static_assert (std::is_same_v<filtereven, std::tuple<char,long,double>>);