How can I use templated Varadic Sequences and Integral Sequence Wrappers?

Hello all,

I've been studying the heck out of the boost metafunction libraries. I understand a good deal of what things like varadic functions and integral sequence wrappers are, but I am having a hard time putting everything together to get working functions, such as performing arithmetic operations or functions like that of std::vector.

Here is an example of what I'm talking about:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Sequences
template<typename T... N> struct seq;
template<typename T, T... N> struct seq_c;

// Integral constant wrapper
template<int T> struct int_
{
   enum { value = T; };
   typedef int_<T> type;
};

// some sequence structure
typedef seq_c<int, 1, 2, 3> mySeq;
 



My knowledge of all of this is pretty scattered and I've really been trying hard to put it all together. Can anyone let me know if this is correct? How can I apply this and use it to do more?
Last edited on
> I am having a hard time putting everything together to get working functions,
> such as performing arithmetic operations or functions like that of std::vector.

Think of recursion, not iteration. For instance:

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
#include <iostream>
#include <type_traits>

template< typename T, T... VALUES  > struct seq_c { using value_type = T ; };

template< typename T, T... > struct size ;
template< typename T, T... VALUES > struct size< seq_c< T, VALUES...> >
{ static constexpr std::size_t value = sizeof...(VALUES) ; } ;

template< typename T > struct pop_front {} ;
template< typename T, T V, T... VALUES > struct pop_front< seq_c< T, V, VALUES...> >
{ using type = seq_c< T, VALUES...> ; } ;

template< std::size_t N, typename T  > struct at {} ;
template< std::size_t N, typename T, T V, T... VALUES > struct at< N, seq_c< T, V, VALUES...> >
{ static constexpr T value = at<N-1, typename pop_front< seq_c< T, V, VALUES...> >::type >::value ; } ;
template< typename T, T V, T... VALUES > struct at< 0, seq_c< T, V, VALUES...> >
{ static constexpr T value = V ; } ;
template< typename T  > struct front
{ static constexpr typename T::value_type value = at<0,T>::value ; } ;

template< typename T, T V, typename U > struct push_back {} ;
template< typename T, T V > struct push_back< T, V, seq_c<T> >
{ using type = seq_c<T,V> ; } ;
template< typename T, T V, T... VALUES > struct push_back< T, V, seq_c< T, VALUES...> >
{ using type = seq_c< T, VALUES..., V > ; } ;

template < typename T, typename U, std::size_t N = size<T>::value > struct paiwise_sum
{
    using common_type = typename std::common_type< typename T::value_type,
                                                   typename U::value_type >::type ;
    using type =  typename push_back<
                         common_type,
                         front<T>::value + front<U>::value,
                         typename paiwise_sum< typename pop_front<T>::type,
                                               typename pop_front<U>::type >::type >::type ;
};
template < typename T, typename U > struct paiwise_sum<T,U,0>
{
    using type = seq_c< typename std::common_type< typename T::value_type,
                                                   typename U::value_type >::type > ; };

template < typename T, T A, T B > struct max
{ static constexpr T value = A < B ? B : A ; } ;
template< typename T > struct max_element {};
template< typename T, T V, T... VALUES > struct max_element< seq_c< T, V, VALUES...> >
{
    using value_type = typename seq_c< T, V, VALUES...>::value_type ;
    static constexpr T value = max< T, V, max_element< seq_c< T, VALUES...> >::value >::value ;
} ;
template< typename T, T V > struct max_element< seq_c< T, V > > { static constexpr T value = V ; } ;

template < typename T > struct print {} ;
template < typename T > struct print< seq_c<T> > {};
template< typename T, T V, T... VALUES >
struct print< seq_c< T, V, VALUES...> > : print< seq_c<T,VALUES...> >
{ print() { std::cout << V << ' ' ; } } ;

int main()
{
    using a = seq_c< long long , 10, 20, 30, 40, 50 > ;
    using b = seq_c< int, -1, -17, -23, 0, -45 > ;
    using c = paiwise_sum<a,b>::type ;
    print<c>{} ;
    std::cout << "  max: " << max_element<c>::value << '\n' ;
}

http://ideone.com/cWsEtf
JLBorges, I appreciate your help once again.

I have a general idea what's going on. I can't help but wonder if using is really necessary? I can't picture boost writing their meta functions in this manner, but I can't say a whole lot since I am still a novice with this. Is there any alternative to the using keyword? I guess I am so confused by this because I've never seen it applied in any of boost's documentation that I've read.

Thanks again for the post.
Here is a clearer idea of a problem that has had me stuck for the past 2 days:

1
2
3
4
5
// A Boost Operation

int number = mpl::plus<mpl::int_<2>, mpl::int_<3>>::type::value;

// 'number' is now equal to 5 


Boost employs a wrapper method that offers polymorphism between types and integral constants. I have no idea how this is structured so that the 2 and 3 can be extracted as values. It is generally easy to get the type, but the value is always killer for me and I'm obviously doing a lot wrong...

My (sad) attempt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template<typename T, T ... > struct integral_c;

template<int N> struct int_
{ typedef int type; };
template<long N> struct long_
{ typedef long type; };

template<typename T, T N> struct integral_c<int_<N>, N>
{ enum { value = N }; };
template<typename T, T N> struct integral_c<long_<N>, N>
{ enum { value = N }; };

template<typename T1, typename T2> struct plus
{
   typedef plus<T1,T2> type;
   // get values?
};
template<typename T1, typename T2> struct plus<int_<T1>, int_<T2>>
{
   // get values?
};


I really hope to learn a good portion of meta programming by the end of the month so I can teach these methods to my friends and other students at school.
> I can't help but wonder if using is really necessary?
> Is there any alternative to the using keyword?

In this case, yes. The alternative is typedef

These two are equivalent:
template< typename T, T... VALUES > struct seq_c { using value_type = T ; };
and
template< typename T, T... VALUES > struct seq_c { typedef T value_type ; };

As are these two:
1
2
template< typename T, T V, T... VALUES > struct pop_front< seq_c< T, V, VALUES...> >
{ using type = seq_c< T, VALUES...> ; } ;

and
1
2
template< typename T, T V, T... VALUES > struct pop_front< seq_c< T, V, VALUES...> >
{ typedef seq_c< T, VALUES...> type ; } ;




> I have no idea how this is structured so that the 2 and 3 can be extracted as values.
> It is generally easy to get the type, but the value is always killer for me

To be able to get the value from the type, the type needs to announce the value.

For example (using typedefs this time):

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
#include <type_traits>

template< typename T, T V, typename = void > struct integral_c ;

// a la std::integral_constant http://en.cppreference.com/w/cpp/types/integral_constant
template< typename T, T V >
struct integral_c< T, V, typename std::enable_if< std::is_integral<T>::value >::type >
{
    static constexpr T value = V ;
    typedef T value_type ;
    typedef integral_c type ;
    constexpr operator value_type() const { return value ; }
};

template< int N > struct int_ : integral_c<int,N> {} ;
template< long N > struct long_ : integral_c<long,N> {} ;

template < typename T, typename U > struct plus : integral_c<
               typename std::common_type< typename T::value_type, typename U::value_type >::type,
               T::value + U::value > {} ;

#include <iostream>
#include <functional>

int main()
{
    typedef int_<5> five ;
    typedef long_<7> seven ;

    typedef plus<five,seven> twelve ;
    static_assert( std::is_same< twelve::value_type, long >::value, "error in common_type" ) ;
    std::cout << twelve::value << '\n' ; // 12

    std::cout << std::minus<long>()( twelve(), five() ) << '\n' // 7
              << twelve() * seven() << '\n' ; // 84
}

http://ideone.com/8OsRQd



> I really hope to learn a good portion of meta programming by the end of the month

Read Alexandrescu's 'Modern C++ Design'
http://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315

And/or Di Gennaro's 'Advanced C++ Metaprogramming'
http://www.amazon.com/Advanced-Metaprogramming-Davide-Di-Gennaro/dp/1460966163/
Last edited on
Thank you for all of your advice, examples, and answers in regards to my questions. Your help has gotten me through a lot of programming issues that I've been encountering lately. Best wishes.
Topic archived. No new replies allowed.