2

I'm trying to implement a way of removing some types from a tuple; for instance I want to be able to e.g. only take a tuple of the first 2 template arguments for a tuple depending on a condition:

  1. Is it possible to 'pack' the types of which a tuple consists back into a parameter pack? (tuple -> typename... contained_types)
  2. Is it possible to combine a param. pack with a typename (e.g. use "Pack1..., Pack2..." specifying a single parameter pack for a struct?
#include <cstdint>
#include <tuple>

template <typename... tpl> struct Helper {
  template <std::size_t rem, typename curr, typename... rest> struct take {
    using type = Helper<(tpl..., curr)>::take<rem-1, rest...>::type; // here, I'm trying (2.)
  };

  template <typename curr, typename... rest> struct take<0, curr, rest...> {
    using type = std::tuple<tpl...>;
  };
};

template <std::size_t s, typename... tpl> using take_t = Helper<>::take<s, tpl...>;

int main() {
  take_t<2, int, int, int> k = std::make_tuple(1, 2);
}

edit The line Helper fails with the following message:

/home/juli/test.cc:6:18: error: need ‘typename’ before ‘Helper<tpl ..., curr>::take’ because ‘Helper<tpl ..., curr>’ is a dependent scope
    6 |     using type = Helper<tpl..., curr>::take<rem-1, rest...>::type;

and when I provide typename

/home/juli/test.cc:6:53: error: expected ‘;’ before ‘<’ token
    6 |     using type = typename Helper<tpl..., curr>::take<rem-1, rest...>::type;

edit2 I achieved this via [helper functions](https://gist.github.com/juliusHuelsmann/669f537aeb5e7105386d510d186b24e1 ), but those fail with non primitive types when the constructor is not constexpr so I cannot use it in my use case and am curious to know how to achieve this and why my approach failed.

mutableVoid
  • 1,284
  • 2
  • 11
  • 29
  • Can't contribute but out of curiosity what's the meaning of ... ? I understand it's variadic, but why typename... tpl and not typename tpl, ... ? – Stefano Borini Feb 18 '20 at 15:08
  • @StefanoBorini See: https://en.cppreference.com/w/cpp/language/parameter_pack – NathanOliver Feb 18 '20 at 15:09
  • @StefanoBorini - variadic pack; expansion or definition – max66 Feb 18 '20 at 15:09
  • @NathanOliver oh my goodness that's horrifying :D thanks. – Stefano Borini Feb 18 '20 at 15:11
  • 1
    So you want to take a `std::tuple` and get a `std::tuple` out of it? – NathanOliver Feb 18 '20 at 15:13
  • 1
    related/dupe: https://stackoverflow.com/questions/17854219/creating-a-sub-tuple-starting-from-a-stdtuplesome-types – NathanOliver Feb 18 '20 at 15:14
  • Yes, that's what I am trying to achieve, sorry for the late reply , but I was having a look at the parameter_pack definition. It would also be fine if I received a set of types and was able to omit a few of them and get a tuple from the first few (so int, double long -> std::tuple. – mutableVoid Feb 18 '20 at 15:15
  • 1
    What was wrong with `Helper`? – user253751 Feb 18 '20 at 15:22
  • I think [This solution](https://stackoverflow.com/a/57537293/12769783) of the question that @NathanOliver linked above works totally fine for me. Thanks to all of you for your input and hint to the solution :) – mutableVoid Feb 18 '20 at 15:48

1 Answers1

0

The answer to Question 2) Yes it is possible, as @user253751 suggested, my original approach (Helper<tpl..., curr>) is correct, I made a different error though, which caused the aforementioned error in that line by omitting 'template' after the used type. The code below shows the fix and works fine:

#include <cstdint>
#include <tuple>

template <typename... tpl> struct Helper {
  template <std::size_t rem, typename curr, typename... rest> struct take {
    using tp = Helper<tpl..., curr>;
    using type = tp::template take<rem-1, rest...>::type;
  };

  template <typename curr, typename... rest> struct take<0, curr, rest...> {
    using type = std::tuple<tpl...>;
  };
};

template <std::size_t s, typename... tpl> using take_t = Helper<>::take<s, tpl...>;

int main() {
  take_t<2, int, int, int>::type k = std::make_tuple(1, 2);
}

The answer to Question 1 is given by the trick performed here, see a full example here. It is possible e.g. by the following construct (adapted from the example on-the-fly and untested):

template <class tuple> struct foo;

template <class ... args> struct foo<std::tuple<args...> {
    // implementation here!
};

which is very neat and can be used for integer_sequence aswell (which I tried and did not succeed to do until now).

An entire implementation of what I wanted to achieve that is much clearer than my initial approach above can be found here:

template <size_t s, typename is, typename ...args> struct thlp;

template <size_t s, size_t... i, class... args> struct thlp<s, std::index_sequence<i...>, args...> {
  static_assert(s <= sizeof...(args), "Requested new tuple size exceeds old one.");
  using type = std::tuple<std::tuple_element_t<i, std::tuple<args...>>...>;
};

template <size_t s, typename... args>
using conditional_tuple_t = thlp<s, decltype(std::make_index_sequence<s>()), args...>::type;
mutableVoid
  • 1,284
  • 2
  • 11
  • 29