Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exclude first n arguments from parameter pack

I have a function foo that calls a function bar with a subset of types passed into foo's variadic template. For example:

template <typename... T>
void foo() {
  // ...
  template <size_t start_idx, typename... T>
  using param_pack = /*Parameter pack with T[start_idx]...T[N]*/
  auto b = bar<param_pack<2, T...>>();
  // ...
}

Is there a way to extract a "sub-parameter pack". In the above case if T = [int float char double] then param_pack<2, T...> = [char double]

[EDIT]

My goal is to be able to use something like this to match event handlers. For example

struct ev {};

template <typename... T>
struct event : ev {
  std::tuple<T...> data_;

  event(T&&... d) : data_(std::make_tuple(std::forward<T>(d)...)) {}
};

template <typename... Functor>
struct handler {
  std::tuple<Functor...> funcs_;

  handler(Functor&&... f) : funcs_(std::make_tuple(std::forward<Functor>(f)...)) {}

  void handle_message(ev* e) {
    auto ptrs = std::make_tuple(
      dynamic_cast<event<param_pack<1, typename function_traits<F>::args>>*>(e)...
    ); 

    match(ptrs);
  }
};

Here function_traits::args get a parameter pack for the function arguments and match iterates over the the tuple funcs_ checking if the dynamic_cast was successful and executing the first successful function. I already have these implemented.

The handlers are something like

[] (handler* self, <ARGS>) -> void {
  // ...
}

I am essentially trying to get rid of the self argument.

like image 401
ssb Avatar asked Oct 23 '25 21:10

ssb


1 Answers

Set aside the fact that it lacks a check on the index N for simplicity, here is a possible solution based on a function declaration (no definition required) and an using declaration:

template<std::size_t N, typename... T, std::size_t... I>
std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>
sub(std::index_sequence<I...>);

template<std::size_t N, typename... T>
using subpack = decltype(sub<N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));

The good part of this approach is that you have not to introduce a new type designed around a tuple, then specialize it somehow iteratively.


It follows a minimal, working example that uses the code above:

#include<functional>
#include<tuple>
#include<cstddef>
#include<type_traits>

template<std::size_t N, typename... T, std::size_t... I>
std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>
sub(std::index_sequence<I...>);

template<std::size_t N, typename... T>
using subpack = decltype(sub<N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));

int main() {
    static_assert(std::is_same<subpack<2, int, float, char, double>, std::tuple<char, double>>::value, "!");
}

See a full example up and running on wandbox.


The extended version that includes a check on the index N would look like this:

template<std::size_t N, typename... T, std::size_t... I>
std::enable_if_t<(N < sizeof...(T)), std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>>
sub(std::index_sequence<I...>);

That is the type you can see in the first example once wrapped in a std::enable_if_t, nothing more. Again, declaration is enough, no definition required.


EDIT

If you want to use your own class template instead of an std::tuple, you can easily modify the code to do that:

#include<functional>
#include<tuple>
#include<cstddef>
#include<type_traits>

template<typename...>
struct bar {};

template<template<typename...> class C, std::size_t N, typename... T, std::size_t... I>
std::enable_if_t<(N < sizeof...(T)), C<std::tuple_element_t<N+I, std::tuple<T...>>...>>
sub(std::index_sequence<I...>);

template<template<typename...> class C, std::size_t N, typename... T>
using subpack = decltype(sub<C, N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));

int main() {
    static_assert(std::is_same<subpack<bar, 2, int, float, char, double>, bar<char, double>>::value, "!");
}

EDIT

According to the code added to the question, the solution above is still valid. You should just define your event class as it follows:

struct ev {};

template <typename>
struct event;

template <typename... T>
struct event<std::tuple<T...>>: ev {
    // ...
};

This way, when you do this:

event<param_pack<1, typename function_traits<F>::args>>

You still get a tuple out of param_pack (that is the subpack using declaration in my example), but it matches the template partial specialization of event and the parameter pack is at your disposal as T....

This is the best you can do, for you cannot put a parameter pack in an using declaration. Anyway it just works, so probably it can solve your issue.

like image 180
skypjack Avatar answered Oct 26 '25 12:10

skypjack