How can I pull variadic template arguments off from the tail instead of the head?

后端 未结 4 1179
离开以前
离开以前 2021-02-04 13:10

For silly reasons I\'ll not go into here, I need the commented out line to work and the line above it it to not work:

template
         


        
相关标签:
4条回答
  • 2021-02-04 13:24

    Here's a solution: Instead of truncating N from the back, I just truncate sizeof...(Args) - N from the front:

    #include <tuple>
    
    /* Concatenator helper */
    
    template <typename T, typename Tuple> struct cat;
    template <typename T, typename ...Args>
    struct cat<T, std::tuple<Args...>>
    {
      typedef typename std::tuple<T, Args...> value;
    };
    
    
    /* Head-of-tuple */
    
    template <unsigned int, typename...> struct tuple_head;
    
    // Base case. Need to specialize twice, once for one and once for variadic types
    template <typename ...Args>
    struct tuple_head<0, Args...>
    {
      typedef std::tuple<> value;
    };
    template <typename T>
    struct tuple_head<0, T>
    {
      typedef std::tuple<> value;
    };
    
    // Recursion step
    template <unsigned int N, typename T, typename ...Args>
    struct tuple_head<N, T, Args...>
    {
      typedef typename cat<T, typename tuple_head<N - 1, Args...>::value>::value value;
    };
    
    
    /* User interface */
    
    template <unsigned int N, typename ...Args>
    struct PartialTuple
    {
      typedef typename tuple_head<sizeof...(Args) - N, Args...>::value type;
    };
    
    
    /* Usage */
    
    #include <string>
    int main()
    {
      // I want this to not work...
      //PartialTuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};
    
      // I want this to work...
      PartialTuple<1, std::string, std::string, int, int>::type B("test", "test", 5);
      PartialTuple<0, std::string, std::string, int, int>::type C("test", "test", 5, 6);
    }
    
    0 讨论(0)
  • 2021-02-04 13:24

    I have done something similar using Boost.MPL and Boost.Fusion: compute the type sequence using the MPL facilities such as push_back, then convert it to a fusion::vector with fusion::as_vector and MPL adaptors. I already had a helper to convert a fusion::vector to std::tuple though.

    0 讨论(0)
  • 2021-02-04 13:25

    I've made my code work a little bit like lists in Haskell - because, well, TMP is purely functional language inside C++.

    add_to_pack is equivalent to Haskell's list constructor (:). drop_from_end is implemented as (in Haskell notation) \x list -> take (length list - x) list, where take n just takes first n elements of the list.

    I suppose you could use std::tuple directly instead of pack, but I liked this solution better, because it doesn't misuse tuple as template parameter pack holder. :)

    Here's the code:

    #include <tuple>
    #include <type_traits> // for std::conditional
    
    
    template <typename... Pack>
    struct pack
    { };
    
    
    template <typename, typename>
    struct add_to_pack;
    
    template <typename A, typename... R>
    struct add_to_pack<A, pack<R...>>
    {
      typedef pack<A, R...> type;
    };
    
    
    template <typename>
    struct convert_to_tuple;
    
    template <typename... A>
    struct convert_to_tuple<pack<A...>>
    {
      typedef std::tuple<A...> type;
    };
    
    
    template <int, typename...>
    struct take;
    
    template <int N>
    struct take<N>
    {
      typedef pack<> type;
    };
    
    template <int N, typename Head, typename... Tail>
    struct take<N, Head, Tail...>
    {
      typedef
        typename std::conditional<
          (N > 0),
          typename add_to_pack<
            Head,
            typename take<
              N - 1,
              Tail...
            >::type
          >::type,
          pack<>
        >::type type;
    };  
    
    
    template <int N, typename... A>
    struct drop_from_end
    {
      // Add these asserts if needed.
      //static_assert(N >= 0,
      //  "Cannot drop negative number of elements!");
    
      //static_assert(N <= static_cast<int>(sizeof...(A)),
      //  "Cannot drop more elements than size of pack!")
    
      typedef
        typename convert_to_tuple<
          typename take<
            static_cast<int>(sizeof...(A)) - N,
            A...
          >::type
        >::type type;
    };
    
    
    int main()
    {
      drop_from_end<2, const char*, double, int, int>::type b{"pi", 3.1415};
    }
    

    And here's the code at work: via ideone.com.


    The take struct is more or less equivalent to following Haskell code:

    take n []     = []
    take n (x:xs)
      | n > 0     = x : take (n - 1) xs
      | otherwise = []
    
    0 讨论(0)
  • 2021-02-04 13:28

    I've been playing with it all night and finally got something to work (changed my casing to match the STL):

    template<uint _N, typename... _All>
    struct reverse_tuple_outer
    {
        template<typename _Head, typename... _Tail>
        struct reverse_tuple_inner: reverse_tuple_outer<_N-1, _Head, _All...>::template reverse_tuple_inner<_Tail...> { };
    };
    
    template<typename... _All>
    struct reverse_tuple_outer<0, _All...>
    {
        template<typename... _Tail>
        struct reverse_tuple_inner {
            typedef std::tuple<_All...> type;
        };
    };
    
    template<typename... _Args>
    struct reverse_tuple
    {
        typedef typename reverse_tuple_outer<sizeof...(_Args)>::template reverse_tuple_inner<_Args...>::type type;
    };
    
    template<typename... _Args>
    struct strip_and_reverse_tuple;
    
    template<typename... _Args>
    struct strip_and_reverse_tuple<std::tuple<_Args...>>
    {
        typedef typename reverse_tuple<_Args...>::type type;
    };
    
    template<uint _N, typename... _Args>
    struct partial_tuple
    {
        typedef typename strip_and_reverse_tuple<typename reverse_tuple_outer<sizeof...(_Args)-_N>::template reverse_tuple_inner<_Args...>::type>::type type;
    };
    
    int main()
    {
        //partial_tuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};
        partial_tuple<1, std::string, std::string, int, int>::type B{"test", "test", 5};
    }
    

    As an added bonus, I also have reverse_tuple, should I ever need it.

    0 讨论(0)
提交回复
热议问题