Search code examples
c++templatesc++11tuples

How to obtain a part of a tuple?


How can I obtain the tuple type of the first elements in a given tuple type?

If I only ask for one element, it should give me the inner type instead of a tuple type of one element.

In code, how would I get the type I'm looking for?

//TupleType is a tuple of n types 0,1,2,...,n-1
template<size_t i, typename TupleType>
struct first_elements {
  using type = //the "sub-tuple" of types 0,1,2,...,i
               //exception: if i=0, just the bare type within
               //the tuple, not the tuple of one element
};

Once finished, I could use it like this:

using my_tuple_type = first_elements<1,decltype(great_tuple)>::type;
using not_tuple_type = first_elements<0,decltype(great_tuple)>::type;

Solution

  • UPDATE

    If you have got here seeking code that will extract a specified subsequence of a given std::tuple and/or define the type of a subsequence of a given std::tuple-type, skip to Anwser to the apparent question. The OP's question is actually not that one.

    Answer to the actual question

    This will do:

    #include <cstddef>
    #include <type_traits>
    #include <tuple>
    
    template<size_t I, typename TupleType, typename Enable = void>
    struct first_elements;
    
    template<size_t I, typename TupleType>
    struct first_elements<I,TupleType,typename std::enable_if<(I == 0)>::type> 
    {
        using type = typename std::tuple_element<0,TupleType>::type;    
    };
    
    template<size_t I, typename TupleType>
    struct first_elements<I,TupleType,typename std::enable_if<(I == 1)>::type> 
    {
        using type = 
        typename std::tuple<typename std::tuple_element<0,TupleType>::type>;        
    };
    
    template<size_t I, typename TupleType>
    struct first_elements<I,TupleType,typename std::enable_if<(I > 1)>::type> 
    {
        using next = 
        typename std::tuple<typename std::tuple_element<I - 1,TupleType>::type>;
        using prev = typename first_elements<I - 1,TupleType>::type;
        using type = decltype(
            std::tuple_cat(std::declval<prev>(),std::declval<next>())
        );
    };
    
    // A test program...
    
    #include <iostream>
    
    using namespace std;
    
    int main(int argc, char **argv)
    {
        auto tup = std::tuple<char,int,float,double>();
        cout << 
        std::is_same<first_elements<0,decltype(tup)>::type,char>::value << endl;
        cout << 
        std::is_same<first_elements<1,decltype(tup)>::type,std::tuple<char>>::value 
        << endl;
        cout << 
        std::is_same<
            first_elements<2,decltype(tup)>::type,
            std::tuple<char,int>>::value 
        << endl;
        cout << 
        std::is_same<
            first_elements<3,decltype(tup)>::type,
            std::tuple<char,int,float>>::value 
        << endl;
        cout << 
        std::is_same<
            first_elements<4,decltype(tup)>::type,
            std::tuple<char,int,float,double>>::value 
        << endl;
        return 0;
    }
    

    No special precautions for compiletime range-errors. They will barf as std::tuple makes them barf.

    (gcc 4.7.2/4.8.1.clang 3.3, -std=c++11)

    Anwser to the apparent question

    #include <type_traits>
    #include <tuple>
    
    /*  A class template to obtain the type and value of the
        the subsequence [First,Last) of a tuple type TupleType
    
        First:- The start of the subsequence [First,Last)
        Last:- The end of the subsequence [First,Last)
        TupleType: - The std::tuple type to be queried.
        Enable:- SFINAE parameter
    
        The public member `type` is defined as the type of the
        subsequence [First,Last) of `TupleType`.
    
        The static member function:
    
        `type get(TupleType const & tup)`
    
        returns the `std::tuple` of the subsequence [First,Last) of `tup`.
        `std::tuple<>` is returned when `First`
        is out of range. The terminal sub-tuple indexed by `First` is
        returned if only `Last` is out of range.
    
    */
    template<
        unsigned First, unsigned Last, typename TupleType, typename Enable = void
    >
    struct tuple_part;
    
    template<unsigned First, unsigned Last, typename TupleType>
    struct tuple_part<
        First,Last,TupleType,
        typename std::enable_if<
            (First >= Last || First >= std::tuple_size<TupleType>::value)
        >::type
    > 
    {
        using type = std::tuple<>;
    
        static constexpr type get(TupleType const & tup) {
            return type();
        }
    };
    
    template<unsigned First, unsigned Last, typename TupleType>
    struct tuple_part<
        First,Last,TupleType,
        typename std::enable_if<
            (Last == First + 1 && First < std::tuple_size<TupleType>::value)
        >::type
    > 
    {
        using type = 
        typename std::tuple<typename std::tuple_element<First,TupleType>::type>;
    
        static constexpr type get(TupleType const & tup) {
            return type(std::get<First>(tup));
        } 
    };
    
    template<unsigned First, unsigned Last, typename TupleType>
    struct tuple_part<
        First,Last,TupleType,
        typename std::enable_if<
            (Last > First + 1 && Last <= std::tuple_size<TupleType>::value)
        >::type
    >
    {
        using head = typename tuple_part<First,First + 1,TupleType>::type;
        using tail = typename tuple_part<First + 1,Last,TupleType>::type;
        using type = decltype(
            std::tuple_cat(std::declval<head>(),std::declval<tail>())
        );
    
        static constexpr type get(TupleType const & tup) {
            return std::tuple_cat(
                        tuple_part<First,First + 1,TupleType>::get(tup),
                        tuple_part<First + 1,Last,TupleType>::get(tup)
                    );
        }
    };
    
    template<unsigned First, unsigned Last, typename TupleType>
    struct tuple_part<
        First,Last,TupleType,
        typename std::enable_if<
            (Last > First + 1 && Last > std::tuple_size<TupleType>::value)
        >::type
    > : tuple_part<First,std::tuple_size<TupleType>::value,TupleType>
    {
        using base_type = 
            tuple_part<First,std::tuple_size<TupleType>::value,TupleType>;
        using type = typename base_type::type;
    };
    
    /*
        `get_part<First,Last>(TupleType const & tup)`
        returns the `std::tuple` of the subsequence [First,Last) of `tup` 
    */ 
    template<unsigned First, unsigned Last, typename TupleType>
    constexpr
    decltype(
        tuple_part<First,Last,TupleType>::get(std::declval<TupleType>())
    )   
    get_part(TupleType const & tup) 
    {
        return tuple_part<First,Last,TupleType>::get(tup);
    }
    
    /*
        `get_part<First>(TupleType const & tup)`
        returns the `std::tuple` of the terminal subsequence of `tup`
        indexed by `First`
    */ 
    template<unsigned First, typename TupleType>
    constexpr
    decltype(
        get_part<First,std::tuple_size<TupleType>::value>(std::declval<TupleType>())
    )   
    get_part(TupleType const & tup) 
    {
        return get_part<First,std::tuple_size<TupleType>::value>(tup);
    }
    
    // A test program...
    
    #include <cassert>
    
    int main(int argc, char **argv)
    {
        using type = std::tuple<char,int,float,double>;
        constexpr type t0(1,2,3.0,4.0);
        constexpr auto p0 = get_part<0,1>(t0);
        assert(p0 == std::tuple<char>(1));
        auto p1 = get_part<0,2>(t0);
        assert((p1 == std::tuple<char,int>(1,2)));
        auto p2 = get_part<0,3>(t0);
        assert((p2 == std::tuple<char,int,float>(1,2,3.0)));
        auto p3 = get_part<0>(t0);
        assert((p3 == std::tuple<char,int,float,double>(1,2,3.0,4.0)));
        auto p4 = get_part<1,2>(t0);
        assert(p4 == std::tuple<int>(2));
        auto p5 = get_part<1,3>(t0);
        assert((p5 == std::tuple<int,float>(2,3.0)));
        auto p6 = get_part<1>(t0);
        assert((p6 == std::tuple<int,float,double>(2,3.0,4.0)));
        auto p7 = get_part<2,3>(t0);
        assert(p7 == std::tuple<float>(3.0));
        auto p8 = get_part<2,4>(t0);
        assert((p8 == std::tuple<float,double>(3.0,4.0)));  
        auto p9 = get_part<3>(t0);
        assert(p9 == std::tuple<double>(4.0));
        auto p10 = get_part<3,5>(t0);
        assert(p10 == std::tuple<double>(4.0));
        auto p11 = get_part<4,4>(t0);
        assert(p11 == std::tuple<>());
        auto p12 = get_part<5,4>(t0);
        assert(p12 == std::tuple<>());  
        return 0;
    }
    
    // EOF
    

    (gcc 4.7.2/4.8.1,clang 3.2/3.3, -std=c++11)