How to create a new tuple type from an old one and a type in boost?

前端 未结 2 513
傲寒
傲寒 2021-02-08 17:28

I have a tuple type. I want to add a element type in it to get a new tuple type. I can do it like

decltype tuple_cat(MyTuple, std::tuple())
         


        
相关标签:
2条回答
  • 2021-02-08 18:05

    C++14 offers a library to generate a sequence of integers at compile type. This helps manipulating static sequences like tuples and arrays (example). An integer sequence can be obtained

    template<size_t... Ints>
    struct integer_sequence {};
    
    template<size_t Size, size_t... Ints>
    struct implementation : implementation<Size-1, Size-1, Ints...> {};
    
    template<size_t... Ints>
    struct implementation<0, Ints...>
    {
        typedef integer_sequence<Ints...> type;
    };
    
    template<class... T>
    using index_sequence_for = typename implementation<sizeof...(T)>::type;
    

    To concat MyTuple and MyType you can write the simple functions:

    template<typename X, typename Tuple, size_t... Ints>
    auto concat(X x, Tuple t, integer_sequence<Ints...>)
        -> decltype( std::make_tuple(x, std::get<Ints>(t)...) )
    {
        return std::make_tuple(x, std::get<Ints>(t)...);
    }
    
    template<typename X, typename... T>
    std::tuple<X, T...> concat(X x, std::tuple<T...> t)
    {
        return concat(x, t, index_sequence_for<T...>());
    }
    
    concat(MyType, MyTuple);
    
    0 讨论(0)
  • 2021-02-08 18:09

    I assume you want all this in compile time.

    Here is the general explanation: concatening tuples is similar to concatening lists or arrays, is that the algorithm is the same. Here, given tuples a and b, I choosed to move the last element of a to the beginning of b, and repeat until a is empty.

    First: base structures. The following structure keeps a parameter pack. It can be anything, for example a tuple:

    template<typename... T>
    struct pack
    {
        static const unsigned int size = sizeof...(T);
    };
    

    Note that the size of the pack is stored inside it. It is not mandatory, but it is convenient for the explanation. Boost uses the struct boost::tuples::length<T>::value (which is more verbose).

    To access an element at i-th position, we use a structure similar to boost::tuples::element<n, T>:

    // Get i-th element of parameter pack
    // AKA 'implementation'
    // Principle: the element i is the first element of the sub-array starting at indice i-1
    template<int n, typename F, typename... T>
    struct element_at : public element_at<n-1, T...>
    {
    };
    
    template<typename F, typename... T>
    struct element_at<0, F, T...>
    {
        typedef F type;
    };
    
    // Get i-th element of pack
    // AKA 'interface' for the 'pack' structure
    template<int n, typename P>
    struct element
    {
    };
    
    template<int n, typename... T>
    struct element<n, pack<T...>>
    {
        typedef typename element_at<n, T...>::type type;
    };
    

    Now, we must use a low-level operation which is adding one element to a side of a pack (adding at left or at right). Here adding at left is choosed, but it is not the only choice:

    // Concat at left (only for structure 'pack')
    template<typename a, typename b>
    struct tuple_concat_left
    {
    };
    
    template<typename a, typename... b>
    struct tuple_concat_left<a, pack<b...>>
    {
        typedef pack<a, b...> type;
    };
    

    For templates, a is not changed, and instead we use an indice to know what element to add. The inheritance define a 'type' typedef which is the concatenation of all indices after n and the other tuple (not including n, and in order). We just have to concatenate at left the element at indice n.

    // Concat 2 tuples
    template<typename a, typename b, int n = 0, bool ok = (n < a::size)>
    struct tuple_concat : public tuple_concat<a, b, n+1>
    {
        typedef typename tuple_concat_left<
            typename element<n, a>::type,
            typename tuple_concat<a, b, n+1>::type
        >::type type;
    };
    
    template<typename a, typename b, int n>
    struct tuple_concat<a, b, n, false>
    {
        typedef b type;
    };
    

    And that's it! Live example here.

    Now, for tuple specifics: you noticed I didn't used boost::tuple nor std::tuple. That is because a lot of implementations of boost tuplesdo not have access to variadic templates, so a fixed number of template parameters is used (they default to boost::tuples::null_type). Putting this directly with variadic templates is a headache, thus the need to have another abstraction.

    I also assumed that you can use C++11 (with the decltype in your question). Concatening 2 tuples in C++03 is possible, but more repetitive and boring.

    You can convert a pack to a tuple really easily: just change the pack definition to:

    template<typename... T>
    struct pack
    {
        static const unsigned int size = sizeof...(T);
        typedef boost::tuple<T...> to_tuple; // < convert this pack to a boost::tuple
    };
    
    0 讨论(0)
提交回复
热议问题