Given a parameter pack, create any new one from it

First basic kind:
1
2
3
4
5
6
template <int OMIT, int TAKE, typename TUPLE>
struct partial_tuple_concrete {
    typename partial_tuple_from_tuple<OMIT, TAKE, TUPLE>::type operator() (const TUPLE& t) const {
	return std::make_tuple (std::get<OMIT>(t), std::get<OMIT + 1>(t), std::get<OMIT + TAKE - 1>(t));  // generalize this!
	}
};

The output is to be a new tuple from t by omitting the first OMIT components and then taking the next TAKE components. But I'm having trouble generalizing the line above. Can anyone help? Here is the code I have so far, which works but is not generalized to a tuple of any length:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <iostream>
#include <tuple>
#include <string>
#include <utility>  // std::conditional

template <typename... PACK>
struct pack {};

template <typename, typename>
struct add_to_pack;

template <typename A, typename... REST>
struct add_to_pack<A, pack<REST...>> {
  	using type = pack<A, REST...>;
};

template <int, typename...>
struct pack_from_back;

template <int N>
struct pack_from_back<N> {
  	using type = pack<>;
};

template <int N, typename FIRST, typename... REST>  // creates a pack by removing the first N types
struct pack_from_back<N, FIRST, REST...> {
    static_assert (N >= 0, "Error.  Cannot remove a negative number of elements.");
    static_assert (N <= static_cast<int>(sizeof...(REST) + 1), "Cannot remove more elements than the size of the pack.");
  	using type = typename pack_from_back<N-1, REST...>::type;
};

template <typename FIRST, typename ...REST>
struct pack_from_back<0, FIRST, REST...> {  // base case
    using type = pack<FIRST, REST...>;
};

template <>
struct pack_from_back<0> {
    using type = pack<>;
};

template <int, typename...>
struct pack_from_front_helper;

template <int N>
struct pack_from_front_helper<N> {
  	using type = pack<>;
};

template <int N, typename FIRST, typename... REST>
struct pack_from_front_helper<N, FIRST, REST...> {
  	using type = typename std::conditional<(N > 0),
	typename add_to_pack<FIRST, typename pack_from_front_helper<N - 1, REST...>::type>::type, pack<>>::type;
};

template <int, typename...> 
struct pack_from_front;

template <int N, typename... TYPES>  // creates a pack from the first N types
struct pack_from_front<N, pack<TYPES...>> {
    static_assert (N >= 0, "Error.  Cannot take a negative number of elements.");
    static_assert (N <= static_cast<int>(sizeof...(TYPES)), "Cannot take more elements than the size of the pack.");
  	using type = typename pack_from_front_helper<N, TYPES...>::type;
};

template <int OMIT, int TAKE, typename ...TYPES>
struct pack_from_middle {
	using type = typename pack_from_front<TAKE, typename pack_from_back<OMIT, TYPES...>::type>::type;
};

template <typename>
struct pack_to_tuple;

template <typename... PACK>
struct pack_to_tuple<pack<PACK...>> {
  	using type = std::tuple<PACK...>;
};

template <int OMIT, int TAKE, typename ...TYPES>
struct partial_tuple {
    using type = typename pack_to_tuple <typename pack_from_middle<OMIT, TAKE, TYPES...>::type>::type;
};

template <int, int, typename...>
struct partial_tuple;

template <int OMIT, int TAKE, typename ...TYPES>
struct partial_tuple<OMIT, TAKE, pack<TYPES...>> {
    using type = typename pack_to_tuple <typename pack_from_middle<OMIT, TAKE, TYPES...>::type>::type;
};

template <typename...>
struct tuple_to_pack;

template <typename... TYPES>
struct tuple_to_pack<std::tuple<TYPES...>> {
	using type = pack<TYPES...>;
};

template <int OMIT, int TAKE, typename TUPLE>
struct partial_tuple_from_tuple {
	using type = typename partial_tuple<OMIT, TAKE, typename tuple_to_pack<TUPLE>::type>::type;
};

template <int OMIT, int TAKE, typename TUPLE>
struct partial_tuple_concrete {
	typename partial_tuple_from_tuple<OMIT, TAKE, TUPLE>::type operator() (const TUPLE& t) const {
		 return std::make_tuple (std::get<OMIT>(t), std::get<OMIT + 1>(t), std::get<OMIT + TAKE - 1>(t));  // generalize this!
	}
};

int main() {
	partial_tuple<2, 3, std::string, int, std::string, double, int, char>::type mytuple ("test", 3.14, 5);
  	std::cout << "mytuple: " << std::get<0>(mytuple) << ", " << std::get<1>(mytuple) << ", " << std::get<2>(mytuple) << std::endl;
  	std::tuple<int, std::string, double, char, std::string, int, double> tuple1 (8, "house", 3.14, 'b', "apple", 6, 1.5);
	partial_tuple_from_tuple<3, 3, decltype(tuple1)>::type tuple2 ('a', "test", 5);
  	std::cout << "tuple2: " << std::get<0>(tuple2) << ", " << std::get<1>(tuple2) << ", " << std::get<2>(tuple2) << std::endl;
  	partial_tuple_from_tuple<3, 3, decltype(tuple1)>::type tuple3 = partial_tuple_concrete<3, 3, decltype(tuple1)>()(tuple1);
  	std::cout << "tuple3: " << std::get<0>(tuple3) << ", " << std::get<1>(tuple3) << ", " << std::get<2>(tuple3) << std::endl;
	std::cin.get();
}

Output:
1
2
3
mytuple: test, 3.14, 5
tuple2: a, test, 5
tuple3: b, apple, 6  // works, but only as a special case 

Or maybe there is a much simpler way that I'm missing? Feel free to avoid reading the mess above if you have a quicker idea. As for an application for this, I'm not sure. I'm just tinkering around and posed this challenge for myself.

Update:
I think I might be on to something using the following index trick I've googled up:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template<int...>
struct index_tuple{}; 

template<int I, typename IndexTuple, typename... TYPES> 
struct make_indexes_impl; 

template<int I, int... INDICES, typename T, typename ... TYPES> 
struct make_indexes_impl<I, index_tuple<INDICES...>, T, TYPES...> { 
    using type = typename make_indexes_impl<I + 1, index_tuple<INDICES..., I>, TYPES...>::type; 
}; 

template<int I, int... INDICES> 
struct make_indexes_impl<I, index_tuple<INDICES...>> { 
    using type = index_tuple<INDICES...>; 
}; 

template<int OMIT, typename ... TYPES> 
struct make_indexes : make_indexes_impl<OMIT, index_tuple<>, TYPES...> {}; 

template<typename... TYPES, int... INDICES> 
std::tuple<TYPES...> create_tuple (index_tuple<INDICES...>, const std::tuple<TYPES...>& tup)  { 
    return std::make_tuple (std::get<INDICES>(tup)...); 
} 

To make create_tuple work, I think I merely need to get corrects values for INDICES, namely CMIT, OMIT + 1, ... , OMIT + TAKE - 1. It's already starting at OMIT, so I might soon be there.
Last edited on
If we just want to extract the subtuple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <tuple>

template < std::size_t SKIP, std::size_t TAKE > struct extract_sub_tuple
{
    template< typename... T >
    auto operator() ( const std::tuple<T...>& tuple ) const 
        -> decltype( std::tuple_cat( std::make_tuple( std::get<SKIP>(tuple) ), extract_sub_tuple< SKIP+1, TAKE-1 >()(tuple) ) ) 
    { return std::tuple_cat( std::make_tuple( std::get<SKIP>(tuple) ), extract_sub_tuple< SKIP+1, TAKE-1 >()(tuple) ) ; }
};

template < std::size_t SKIP > struct extract_sub_tuple< SKIP, 0 >
{
    template< typename... T >
    std::tuple<> operator() ( const std::tuple<T...>& ) const { return std::tuple<>() ; }
};

int main()
{

    std::tuple< char, char16_t, char32_t, wchar_t, short, long, long long > tup( 0, 1, 2, 3, 4, 5, 6 ) ;
    auto sub_tuple = extract_sub_tuple<2,4>()(tup) ;
    std::cout << std::boolalpha << ( sub_tuple == std::make_tuple(2,3,4,5) ) <<  '\n' ;
}

http://coliru.stacked-crooked.com/a/28bbc4c7c569e211

If we also want to extract the type:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <tuple>
#include <type_traits>

template < typename... > struct pop_front ;

template < typename FIRST, typename... REST >
struct pop_front< std::tuple< FIRST, REST... > > { using type = std::tuple<REST...> ; };
// ----------------------------------------------------------------

template < typename... > struct push_front ;

template < typename T, typename... U >
struct push_front< T, std::tuple< U... > > { using type = std::tuple< T, U... > ; };
// ----------------------------------------------------------------

template < std::size_t SKIP, typename... T > struct tail ;

template < std::size_t SKIP, typename...T >
struct tail< SKIP, std::tuple<T...> >
{
    static_assert( std::tuple_size< std::tuple<T...> >::value > SKIP, "tuple is too small" ) ;
    using type = typename tail< SKIP-1, typename pop_front< std::tuple<T...> >::type >::type ;
};

template < typename...T > struct tail< 0, std::tuple<T...> > { using type = std::tuple<T...>; };
// ----------------------------------------------------------------

template < std::size_t TAKE, typename... T > struct head ;

template < std::size_t TAKE, typename FIRST, typename... REST >
struct head< TAKE, std::tuple< FIRST, REST... > >
{
    static_assert( std::tuple_size< std::tuple<FIRST,REST...> >::value >= TAKE, "tuple is too small" ) ;
    using type = typename push_front< FIRST, typename head< TAKE-1, std::tuple<REST...> >::type >::type ;
};

template < typename FIRST, typename... REST >
struct head< 1, std::tuple< FIRST, REST... > > { using type = std::tuple<FIRST> ; };
// ----------------------------------------------------------------

template < std::size_t SKIP, std::size_t TAKE, typename... > struct sub_tuple ;

template < std::size_t SKIP, std::size_t TAKE, typename... T > struct sub_tuple< SKIP, TAKE, std::tuple<T...> >
{
    using type = typename head< TAKE, typename tail< SKIP, std::tuple<T...> >::type >::type ;
};
// ----------------------------------------------------------------

template < std::size_t SKIP, std::size_t TAKE > struct extract_sub_tuple
{
    template< typename... T >
    typename sub_tuple< SKIP, TAKE, std::tuple<T...> >::type operator() ( const std::tuple<T...>& tuple ) const
    { return std::tuple_cat( std::make_tuple( std::get<SKIP>(tuple) ), extract_sub_tuple< SKIP+1, TAKE-1 >()(tuple) ) ; }
};

template < std::size_t SKIP > struct extract_sub_tuple< SKIP, 0 >
{
    template< typename... T >
    std::tuple<> operator() ( const std::tuple<T...>& ) const { return std::tuple<>() ; }
};
// ----------------------------------------------------------------

int main()
{
    using type = sub_tuple< 2, 4, std::tuple< char, char16_t, char32_t, wchar_t, short, int, long, long long > >::type ;
    static_assert( std::is_same< type, std::tuple< char32_t, wchar_t, short, int > >::value, "not ok" ) ;

    std::tuple< char, char16_t, char32_t, wchar_t, short, long, long long > tup( 0, 1, 2, 3, 4, 5, 6 ) ;
    type sub_tuple = extract_sub_tuple<2,4>()(tup) ;
    std::cout << std::boolalpha << ( sub_tuple == std::make_tuple(2,3,4,5) ) <<  '\n' ;
}

http://coliru.stacked-crooked.com/a/adb4b528c287c6fe
Inspired by JLBorges's improvement, here I wrote
template <int...I>
struct permute_tuple;
such that permute_tuple::type creates a tuple where the std::get<0> is now the previous std::get<I1>, std::get<1> is now the previous std::get<I2>, where I... = <I1, I2, ...>. Those not mentioned in I... means that those components are removed. (Hence the original question is just a special case of this, and this covers all possible subtuples).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <tuple>
#include <string>

template <int FIRST, int... REST>
struct permute_tuple {
	template <typename TUPLE>
	auto operator() (const TUPLE& t) const -> decltype (std::tuple_cat (std::make_tuple (std::get<FIRST>(t)), permute_tuple<REST...>()(t))) {
		return std::tuple_cat (std::make_tuple (std::get<FIRST>(t)), permute_tuple<REST...>()(t));
	}
};

template <int LAST>
struct permute_tuple <LAST> {
	template <typename TUPLE>
	auto operator() (const TUPLE& t) const -> decltype (std::make_tuple (std::get<LAST>(t))) {
		return std::make_tuple (std::get<LAST>(t));
	}
};

int main() {
  	std::tuple<int, std::string, double, char, std::string, int, double> t (8, "house", 3.14, 'b', "apple", 6, 1.5);
  	auto tuple_permuted = permute_tuple<3,5,4,0,6,2,1>()(t);
  	std::cout << "(" << std::get<0>(tuple_permuted) << ", " << std::get<1>(tuple_permuted) << ", " << std::get<2>(tuple_permuted) << ", " << std::get<3>(tuple_permuted) << ", "
	  	<< std::get<4>(tuple_permuted) << ", " << std::get<5>(tuple_permuted) << ", " << std::get<6>(tuple_permuted) << ")" << std::endl;  // (b, 6, apple, 8, 1.5, 3.14, house)
	auto tuple_permuted_some_omitted = permute_tuple<3,4,5>()(t);  // special case of first question
	std::cout << "(" << std::get<0>(tuple_permuted_some_omitted) << ", " << std::get<1>(tuple_permuted_some_omitted) << ", " << std::get<2>(tuple_permuted_some_omitted) << ")" << std::endl;  // (b, apple, 6)
 	std::cin.get();
}

Output:
(b, 6, apple, 8, 1.5, 3.14, house)
(b, apple, 6)

However, I think I will have to give up trying to extract the resulting type. That is just too hard! At least for me.
Edit: Actually, I think I got it. I just have to use my add_to_pack defined in my opening post and use std::tuple_element recursively.
Last edited on
Ever heard of comments?

First thoughts were using MIN MAX predicate to find_if using a custom evaluators and just copy to a new container but that doesn't jive for school does it.

'Cause on the flip side, I'd be hard pressed to want to take on the initiative in just using it.
I guess I'm too need oriented.
Last edited on
> Actually, I think I got it.
> I just have to use my add_to_pack defined in my opening post and use std::tuple_element recursively.

Or let the compiler do the work of deducing and reporting the type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <tuple>
#include <string>
#include <utility>
#include <type_traits>

template <int FIRST, int... REST>
struct select_tuple {
    template <typename TUPLE>
	auto operator() (const TUPLE& t) const 
            -> decltype (std::tuple_cat (std::make_tuple (std::get<FIRST>(t)), select_tuple<REST...>()(t))) {
		return std::tuple_cat (std::make_tuple (std::get<FIRST>(t)), select_tuple<REST...>()(t));
	}

	template< typename TUPLE > struct result_type {
	    using type = decltype( select_tuple<FIRST,REST...>()( std::declval<TUPLE>() ) ) ; };
};

template <int LAST>
struct select_tuple <LAST> {
	template <typename TUPLE>
	auto operator() (const TUPLE& t) const -> decltype (std::make_tuple (std::get<LAST>(t))) {
		return std::make_tuple (std::get<LAST>(t));
	}

	template< typename TUPLE > struct result_type {
	    using type = decltype( select_tuple<LAST>()( std::declval<TUPLE>() ) ) ; };
};

int main() {
    using tuple_type = std::tuple<int, std::string, double, char, std::string, int, double> ;
    tuple_type t (8, "house", 3.14, 'b', "apple", 6, 1.5);

    auto result = select_tuple<1,4,6,2>()(t) ;
    using expected_type = std::tuple<std::string,std::string,double,double> ;
    using computed_type = select_tuple<1,4,6,2>::result_type<tuple_type>::type ;

    static_assert( std::is_same< decltype(result), expected_type >::value, "not ok" );
    static_assert( std::is_same< decltype(result), computed_type >::value, "not ok" );

    std::cout << "ok\n" ;
}

http://coliru.stacked-crooked.com/a/bdb591a673c66182
Ok, now some transformations on a tuple that, unlike the above cool stuff, are independent of the tuple's size: reverse_tuple, alternating_tuple<START, INTERVAL>, rotate_tuple <SHIFT> (all the types are extracted):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <iostream>
#include <tuple>
#include <string>

// reverse_tuple (const TUPLE& t) creates a tuple whose components are in the reverse order of t's components.

template <int N>
struct reverse_tuple_helper {
	template <typename TUPLE>
	auto operator() (const TUPLE& t) const -> decltype (std::tuple_cat (std::make_tuple (std::get<N-1>(t)), reverse_tuple_helper<N-1>()(t))) {
		return std::tuple_cat (std::make_tuple (std::get<N-1>(t)), reverse_tuple_helper<N-1>()(t));	
	}
};

template <>
struct reverse_tuple_helper<0> {
	template <typename TUPLE>
	std::tuple<> operator() (const TUPLE& t) const {return std::tuple<>();}
};

template <typename TUPLE>
struct reverse_tuple_type {
	using type = decltype (reverse_tuple_helper<std::tuple_size<TUPLE>::value>()(std::declval<TUPLE>()));
};

struct reverse_tuple {
	template <typename TUPLE>
	typename reverse_tuple_type<TUPLE>::type operator() (const TUPLE& t) const {
		return reverse_tuple_helper<std::tuple_size<TUPLE>::value>()(t);
	}
};

//	----------------------------------------------------------------------------------------------------------------------------------------------------------
//  alternating_tuple<START, INTERVAL>()(const TUPLE& t) creates a tuple which starts at std::get<START>(t) and then skips every INTERVAL components of t, returning back to the beginning and continuing to the starting component if necessary.

template <int NUM_LEFT, int START, int INTERVAL>
class alternating_tuple_helper {
	private:
		static constexpr int positiveModulo (int i, int n) {return (i % n + n) % n;}  // constexpr needed
		template <typename TUPLE>
		static constexpr int start() {return positiveModulo (START, std::tuple_size<TUPLE>::value);}  // constexpr needed
	public:
		template <typename TUPLE>
		auto operator() (const TUPLE& t) const -> decltype (std::tuple_cat (std::make_tuple (std::get<start<TUPLE>()>(t)), alternating_tuple_helper<NUM_LEFT - 1, START + INTERVAL, INTERVAL>()(t))) {
			return std::tuple_cat (std::make_tuple (std::get<start<TUPLE>()>(t)), alternating_tuple_helper<NUM_LEFT - 1, START + INTERVAL, INTERVAL>()(t));
		}
};

template <int START, int INTERVAL>
class alternating_tuple_helper<0, START, INTERVAL> {
	public:
		template <typename TUPLE>
		std::tuple<> operator() (const TUPLE& t) const {return std::tuple<>();}
};

template <typename TUPLE, int START = 0, int INTERVAL = 2>
struct alternating_tuple_type {
	using type = decltype (alternating_tuple_helper<(std::tuple_size<TUPLE>::value - 1) / INTERVAL + 1, START, INTERVAL>()(std::declval<TUPLE>()));
};

template <int START = 0, int INTERVAL = 2>
struct alternating_tuple {
	template <typename TUPLE>
	typename alternating_tuple_type<TUPLE, START, INTERVAL>::type operator() (const TUPLE& t) const {
		return alternating_tuple_helper<(std::tuple_size<TUPLE>::value - 1) / INTERVAL + 1, START, INTERVAL>()(t);
	}
};

//	----------------------------------------------------------------------------------------------------------------------------------------------------------
// rotate_tuple <SHIFT> (const TUPLE& t) shifts every component of t to the left by SHIFT (if SHIFT is negative, then to the right instead), with the components going off the beginning going to the end.

template <int NUM_LEFT, int SHIFT>
class rotate_tuple_helper {
	private:
		static constexpr int positiveModulo (int i, int n) {return (i % n + n) % n;}  // constexpr needed
		template <typename TUPLE>
		static constexpr int shift() {return positiveModulo (SHIFT, std::tuple_size<TUPLE>::value);}  // constexpr needed
	public:
		template <typename TUPLE>
		auto operator() (const TUPLE& t) const -> decltype (std::tuple_cat (std::make_tuple (std::get<shift<TUPLE>()>(t)), rotate_tuple_helper<NUM_LEFT-1, SHIFT+1>()(t))) {	
			return std::tuple_cat (std::make_tuple (std::get<shift<TUPLE>()>(t)), rotate_tuple_helper<NUM_LEFT-1, SHIFT+1>()(t));
		}
};

template <int SHIFT>
class rotate_tuple_helper<0, SHIFT> {
	public:
		template <typename TUPLE>
		std::tuple<> operator() (const TUPLE& t) const {return std::tuple<>();}	
};

template <typename TUPLE, int SHIFT>
struct rotate_tuple_type {
	using type = decltype (rotate_tuple_helper<std::tuple_size<TUPLE>::value, SHIFT>()(std::declval<TUPLE>()));
};

template <int SHIFT>
struct rotate_tuple {
	template <typename TUPLE>
	typename rotate_tuple_type<TUPLE, SHIFT>::type operator() (const TUPLE& t) const {
		return rotate_tuple_helper<std::tuple_size<TUPLE>::value, SHIFT>()(t);
	}
};
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//	Testing the results

template <std::size_t N, typename TUPLE>
struct TuplePrinter {
	std::ostream& operator() (const TUPLE& t, std::ostream& os = std::cout) {
		TuplePrinter<N-1, TUPLE>()(t);
		os << ", " << std::get<N-1>(t);
		return os;
	}
};

template <typename TUPLE>
struct TuplePrinter<1, TUPLE> {
	std::ostream& operator() (const TUPLE& t, std::ostream& os = std::cout) {
		os << std::get<0>(t);
		return os;
	}
};

template <typename TUPLE>
void printTuple (const TUPLE& t, std::ostream& os = std::cout) {
	os << "(";
	TuplePrinter<std::tuple_size<TUPLE>::value, TUPLE>()(t);
	os << ")" << std::endl;
}

int main() {
	using tuple_type = std::tuple<int, std::string, double, char, std::string, int, double, char>;
    using reverse_type = reverse_tuple_type<tuple_type>::type;
    using alternating_type1 = alternating_tuple_type<tuple_type>::type;
  	using alternating_type2 = alternating_tuple_type<tuple_type,1>::type;
  	using alternating_type3 = alternating_tuple_type<tuple_type,1,3>::type;
  	using alternating_type4 = alternating_tuple_type<tuple_type,2,5>::type;
  	using rotate_tuple_type1 = rotate_tuple_type<tuple_type, 3>::type;
  	using rotate_tuple_type2 = rotate_tuple_type<tuple_type, -2>::type;
  	using rotate_tuple_type3 = rotate_tuple_type<tuple_type, 0>::type;
  	using rotate_tuple_type4 = rotate_tuple_type<tuple_type, 20>::type;
	using tuple_permuted_type = select_subtuple<3,5,4,0,6,2,1,7>::result_type<tuple_type>::type;
  	using tuple_permuted_some_omitted_type = select_subtuple<3,4,5>::result_type<tuple_type>::type;
  	const tuple_type t (8, "house", 3.14, 'b', "apple", 6, 1.5, '!');
  	const reverse_type tuple_reversed = reverse_tuple(t);
  	const alternating_type1 tuple_alternating1 = alternating_tuple<>()(t);
    const alternating_type2 tuple_alternating2 = alternating_tuple<1>()(t);
    const alternating_type3 tuple_alternating3 = alternating_tuple<1,3>()(t);
    const alternating_type4 tuple_alternating4 = alternating_tuple<2,5>()(t);
    const rotate_tuple_type1 tuple_rotated1 = rotate_tuple<3>()(t);
    const rotate_tuple_type2 tuple_rotated2 = rotate_tuple<-2>()(t);
    const rotate_tuple_type3 tuple_rotated3 = rotate_tuple<0>()(t);
    const rotate_tuple_type4 tuple_rotated4 = rotate_tuple<20>()(t);
	const tuple_permuted_type tuple_permuted = select_subtuple<3,5,4,0,6,2,1,7>()(t);
	const tuple_permuted_some_omitted_type tuple_permuted_some_omitted = select_subtuple<3,4,5>()(t);
  	std::cout << "original tuple = ";  printTuple(t);  // (8, house, 3.14, b, apple, 6, 1.5, !)
	std::cout << "tuple_reversed = ";  printTuple (tuple_reversed);  // ('!', 1.5, 6, apple, b, 3.14, house, 8)
  	std::cout << "tuple_alternating1 = ";  printTuple (tuple_alternating1);  // (8, 3.14, apple, 1.5)
  	std::cout << "tuple_alternating2<1> = ";  printTuple (tuple_alternating2);  // (house, b, 6, !)
  	std::cout << "tuple_alternating3<1,3> = ";  printTuple (tuple_alternating3);  // (house, apple, !)
  	std::cout << "tuple_alternating4<2,5> = ";  printTuple (tuple_alternating4);  // (3.14, !)
  	std::cout << "rotate_tuple_type1<3> = ";  printTuple (tuple_rotated1);  // (b, apple, 6, 1.5, !, 8, house, 3.14)
  	std::cout << "rotate_tuple_type1<-2> = ";  printTuple (tuple_rotated2);  // (1.5, !, 8, house, 3.14, b, apple, 6)
  	std::cout << "rotate_tuple_type1<0> = ";  printTuple (tuple_rotated3);  // (8, house, 3.14, b, apple, 6, 1.5, !)
  	std::cout << "rotate_tuple_type1<20> = ";  printTuple (tuple_rotated4);  // (apple, 6, 1.5, !, 8, house, 3.14, b)
  	std::cout << "tuple_permuted<3,5,4,0,6,2,1,7> = ";  printTuple (tuple_permuted);  // (b, 6, apple, 8, 1.5, 3.14, house, !)
	std::cout << "tuple_permuted_some_omitted<3,4,5> = ";  printTuple (tuple_permuted_some_omitted);  // (b, apple, 6)
	std::cin.get();
}

Output:
1
2
3
4
5
6
7
8
9
10
11
12
original tuple = (8, house, 3.14, b, apple, 6, 1.5, !)
tuple_reversed = (!, 1.5, 6, apple, b, 3.14, house, 8)
tuple_alternating1 = (8, 3.14, apple, 1.5)
tuple_alternating2<1> = (house, b, 6, !)
tuple_alternating3<1,3> = (house, apple, !)
tuple_alternating4<2,5> = (3.14, !)
rotate_tuple_type1<3> = (b, apple, 6, 1.5, !, 8, house, 3.14)
rotate_tuple_type1<-2> = (1.5, !, 8, house, 3.14, b, apple, 6)
rotate_tuple_type1<0> = (8, house, 3.14, b, apple, 6, 1.5, !)
rotate_tuple_type1<20> = (apple, 6, 1.5, !, 8, house, 3.14, b)
tuple_permuted<3,5,4,0,6,2,1,7> = (b, 6, apple, 8, 1.5, 3.14, house, !)
tuple_permuted_some_omitted<3,4,5> = (b, apple, 6)
All that remains now is to write a composing function that composes any number of the above functions and give the type and resulting tuple. Something like
1
2
3
4
5
const auto compose = compose_functions (reverse_tuple, {alternating_tuple<2,3>, 30},
    rotate_tuple<5>, select_subtuple<1,3,5>, reverse_tuple);
const std::tuple<int, std::string, double, char, std::string, int, double, char>
    t (8, "house", 3.14, 'b', "apple", 6, 1.5, '!');
auto resulting_tuple = compose(t);


Does that even make any sense, before I rack my brain over trying to write that???
{alternating_tuple<2,3>, 30} means that alternating_tuple<2,3> is to be applied 30 times! Or perhaps forget that part for now.
Here
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
template <typename F0, typename... F>
class Composer {
	private:
	    F0 first;
	    Composer<F...> rest;
	public:
	    Composer(F0 f0, F... f) : first (f0), rest (f...) {}
	    template <typename T>
	    auto operator() (const T& x) const -> decltype(first(rest(x))) {
	        return first (rest(x));
	    }
	    template <typename T>
	    struct result_type {
			using type = decltype(first(rest(std::declval<T>())));
		};
};

template <typename F>
class Composer<F> {
	private:
	    F function;
	public:
	    Composer(F f) : function (f) {}
	    template <typename T>
	    auto operator() (const T& x) const -> decltype(function(x)) {
	        return function(x);
	    }
	    template <typename T>
	    struct result_type {
	    	using type = decltype(function(std::declval<T>()));
		};
};

template <typename... F>
Composer<F...> compose_functions (F... f) {
    return Composer<F...>(f...);
}

int main() {
    using tuple_type = std::tuple<int, std::string, double, char, std::string, int, double, char>;
    const tuple_type t (8, "house", 3.14, 'b', "apple", 6, 1.5, '!');
    using Composer1 = Composer<alternating_tuple<2,3>, rotate_tuple<5>, select_subtuple<1,3,5,4,2,0,7>>;
    using composed_type1 = Composer1::result_type<tuple_type>::type;  // final type obtained too!
    const Composer1 compose1 = compose_functions (alternating_tuple<2,3>(), rotate_tuple<5>(), select_subtuple<1,3,5,4,2,0,7>());
    const composed_type1 composed_tuple1 = compose1(t);
    using Composer2 = Composer<reverse_tuple, alternating_tuple<>, rotate_tuple<5>, select_subtuple<7,1,3,5,4,2,0,6>, reverse_tuple>;
    using composed_type2 = Composer2::result_type<tuple_type>::type;  // final type obtained too!
    const Composer2 compose2 = compose_functions (reverse_tuple(), alternating_tuple<>(), rotate_tuple<5>(), select_subtuple<7,1,3,5,4,2,0,6>(), reverse_tuple());
    const composed_type2 composed_tuple2 = compose2(t);
    std::cout << "composed_tuple1 = ";  printTuple (composed_tuple1);  // (house, apple)
    std::cout << "composed_tuple2 = ";  printTuple (composed_tuple2);  // (3.14, 1.5, house, 6)
}

works fine. Now, I hope I can write the "exponents type", e.g.
compose_functions (..., {alternating_tuple<2,3>, 30}, {rotate_tuple<4>, 10}, ...)
meaning that rotate_tuple<4> is to be applied 10 times, followed by alternating_tuple<2,3> being applied 30 times.
Last edited on
Ok, I wrote a SelfComposer class that composes a function with itself any number of times, and it has passed with all the basic callable entities: global function, member function, function object, and lambda function. But it doesn't work with rotate_tuple<5>() (function object). Now rotate_tuple<5>().maps from a tuple type to another tuple type each time, so I made sure that I generalized the self_compose function to take care of that. Yet it still doesn't work. Can anyone shed some light on this? rotate_tuple<N> has been defined (and tested thoroughly) in a previous post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
template <int N, typename F>
class SelfComposer {
	private:
	  	F function;
		template <typename T, int M> struct result_type { 
    using type = decltype(function(std::declval<typename result_type<T, M-1>::type>())); 
                                 };
		template <typename T> struct result_type<T, 0> { using type = T; };
	public:
	  	SelfComposer (F f) : function (f) {}
		template <typename T>
	  	typename result_type<T, N>::type operator()(T x) const {
	    	    int n = N;
	    	    while (n--)
		        x = function (x);
	    	    return x;
		}
};

template <int N, typename F>
SelfComposer<N, F> self_compose (F f) {
  	return SelfComposer<N, F>(f);
}

int square (int x) {return x * x;}  // test with global function

template <int N>
class Foo {  // test with (non-static) class member function
	public:
		int increaseByOne (int x) {return x + 1;}
		void doIt (int x) {
			x = self_compose<N> (std::bind (&Foo::increaseByOne, this, std::placeholders::_1))(x);
			std::cout << "doIt() = " << x << std::endl;
		}
};

template<int N>
struct functionObject {  // test with function object
	int operator() (int x) const {return N * x;}
};

int main() {
	std::cout << self_compose<4>(square)(3) << std::endl;  // (((3^2)^2)^2)^2 = 3^16 = 43,046,721
	Foo<10> foo;
	foo.doIt(5);  // 15
	Foo<8>* goo = new Foo<8>;
	std::cout << self_compose<7>(std::bind(&Foo<8>::increaseByOne, goo, std::placeholders::_1))(-1) << std::endl;  // 6
	std::cout << self_compose<5>(functionObject<2>())(4);  // 2*2*2*2*2*4 = 128
	std::cout << self_compose<4>([](int x)->int {return x-1;})(0) << std::endl;  // -4 (lambda function)	
  	const std::tuple<int, std::string, double, char, std::string, int, double, char>
	  	t (8, "house", 3.14, 'b', "apple", 6, 1.5, '!');
//	auto rotatedTuple = self_compose<8>(rotate_tuple<5>())(t);  // This line will not compile.  Why???
}


Edit: Ah! I see what the problem is: rotate_tuple<5>() has a deduced template type T, which is changing each time, so it is not the same rotate_tuple<5>() each time after all. Hence SelfComposer, which handles the same function repeatedly, cannot handle rotate_tuple<5>(), but some other class that needs to be written. Of course, self_compose<8>(rotate_tuple<5>())(t) would simply be rotate_tuple<40>()(t), but I'm seeking a generic solution here (for example how to treat self_compose<8> of (rotate_tuple<5>() x reverse_tuple())?) As far as I can find, no one has written a generic solution to this problem.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <tuple>

template < typename FN, std::size_t N > struct do_apply_n
{
    template < typename ARG >
    static auto apply( FN fn, ARG arg ) -> decltype( fn( do_apply_n<FN,N-1>::apply(fn,arg) ) )
    { return fn( do_apply_n<FN,N-1>::apply(fn,arg) ) ; }
};

template < typename FN > struct do_apply_n<FN,1U>
{
    template < typename ARG >
    static auto apply( FN fn, ARG arg ) -> decltype( fn(arg) ) { return fn(arg) ; }
};

template < std::size_t N, typename FN, typename ARG >
auto apply_n( FN fn, ARG arg ) -> decltype( do_apply_n<FN,N>::apply( fn, arg ) )
{ return do_apply_n<FN,N>::apply( fn, arg ) ; }

struct twice
{
    template < typename T >
    auto operator() ( T a ) const -> decltype( std::tuple_cat( a, a ) )
    { return std::tuple_cat( a, a ) ; }
};

int main()
{
    std::tuple<int,double,char> tuple( 1, 2.3, 'A' ) ;
    
    // there will be a huge number of nested template instantiations
    // so, with g++, compile with something like -ftemplate-depth=2048
    const auto result = apply_n<5>( twice(), tuple ) ;
    
    std::cout << std::tuple_size< decltype(result) >() << '\n' ; // 96 ( 3 * 2^5 )
}

http://coliru.stacked-crooked.com/a/b37e24618cc31477
http://rextester.com/CRP39505
Ok, I think we are done then. And I thought of a very general application for this. If you have a parameter pack, and you want any sub-pack of it, in whatever order, indedendent of its size, you can simply convert the pack to a tuple via my earlier mentioned pack_to_tuple, and then make any of the tuple transformations mentioned in this whole thread, and then with the resulting tuple type transform it back to a pack via tuple_to_pack. So the lack of parameter pack helper functions in c++ can be filled with the tools formed in this thread. Many thanks to JLBorges, of course.
Topic archived. No new replies allowed.