2

J'ai une fonction foo qui appelle une fonction bar avec un sous-ensemble de types transmis au modèle variadique foo. Par exemple:Exclure les n premiers arguments du pack de paramètres

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...>>(); 
    // ... 
} 

Existe-t-il un moyen d'extraire un "pack de sous-paramètres". Dans le cas ci-dessus si T = [int float char double] puis param_pack<2, T...> = [char double]

[EDIT]

Mon objectif est d'être en mesure d'utiliser quelque chose comme ça pour correspondre à des gestionnaires d'événements. Par exemple

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); 
    } 
}; 

Ici function_traits::args obtenir un pack de paramètres pour les arguments de la fonction et itère match sur le tuple funcs_ vérifier si le dynamic_cast a réussi et l'exécution de la première fonction avec succès. Je les ai déjà implémentés.

Les gestionnaires sont quelque chose comme

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

Je suis essentiellement en train de se débarrasser de l'argument self.

+0

Une question connexe: https://stackoverflow.com/questions/8569567/get-part-of-stdtuple – keith

+0

Oops ... Mon mal ... Faire un modifier maintenant – subzero

Répondre

3

Mettez de côté le fait qu'il n'a pas de contrôle sur l'indice N pour la simplicité, voici une solution basée sur une déclaration de fonction (pas de définition nécessaire) et un en utilisant la déclaration:

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>{})); 

La bonne partie de cette approche est que vous avez de ne pas introduire un nouveau type conçu autour d'un tuple, se spécialisent alors il itérativement en quelque sorte.


Il suit un minimum, par exemple de travail qui utilise le code ci-dessus:

#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, "!"); 
} 

Voir un exemple complet et en cours d'exécution sur wandbox.


La version étendue qui comprend un contrôle sur l'indice N ressemblerait à ceci:

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...>); 

C'est le type que vous pouvez voir dans le premier exemple une fois enveloppé dans une std::enable_if_t, rien de plus. Encore une fois, la déclaration est suffisante, aucune définition requise.


EDIT

Si vous voulez utiliser votre propre modèle de classe au lieu d'un std::tuple, vous pouvez facilement modifier le code pour le faire:

#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

Selon le code ajouté à la question, la solution ci-dessus est toujours valable. Vous devez simplement définir votre classe event comme suit:

struct ev {}; 

template <typename> 
struct event; 

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

De cette façon, quand vous faites ceci:

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

Vous obtenez toujours un tuple de param_pack (qui est le subpack en utilisant la déclaration dans mon exemple), mais il correspond à la spécialisation partielle du modèle event et le pack de paramètres est à votre disposition en tant que T....

C'est le meilleur que vous pouvez faire, car vous ne pouvez pas mettre un paquet de paramètres dans une déclaration d'utilisation. Quoi qu'il en soit, cela fonctionne, donc probablement, il peut résoudre votre problème.

+0

Le type est un 'std :: tuple ' est-il un moyen d'obtenir le pack directement à savoir, 'T ...' – subzero

+0

@subzero Vous ne pouvez pas mettre un paquet dans une déclaration using.Tout ce que vous pouvez passer est le type que vous voulez remplir avec les autres données et le récupérer à la place d'un tuple.Peut-il fonctionner pour vous? Comment voulez-vous utiliser ce pack? – skypjack

+0

@subzero J'ai ajouté un autre exemple pour montrer comment vous pouvez utiliser votre propre modèle de classe.Mettez-moi si cela fonctionne pour vous – skypjack

2

Vous pouvez faire quelque chose comme:

template <std::size_t N, typename ... Ts> struct drop; 

template <typename ... Ts> 
struct drop<0, Ts...> 
{ 
    using type = std::tuple<Ts...>; 
}; 

template <std::size_t N, typename T, typename ... Ts> 
struct drop<N, T, Ts...> 
{ 
    using type = typename drop<N - 1, Ts...>; 
}; 

// Specialization to avoid the ambiguity 
template <typename T, typename... Ts> 
struct drop<0, T, Ts...> 
{ 
    using type = std::tuple<T, Ts...>; 
}; 
+0

J'ai essayé de mettre en œuvre l'idée similaire mais coincé avec le fait que le compilateur ne peut pas choisir la spécialisation pour 'N = 0'. Pourriez-vous s'il vous plaît jeter un oeil: https://wandbox.org/permlink/u3QOECdxokexLgyG –

+0

@EdgarRokyan: En effet, une spécialisation supplémentaire est nécessaire. [Version corrigée] (https://wandbox.org/permlink/K0Bb3MwjwTtJFU8x). – Jarod42

+0

Parfait, merci! Bien qu'il semble un peu gênant maintenant :( –

0

Voici une solution rapide mais pas particulièrement réutilisable.

template <typename Pack, std::size_t N, std::size_t... Is> 
void invoke_bar_impl(std::index_sequence<Is...>) { 
    bar<std::tuple_element_t<N + Is, Pack>...>(); 
} 

template <std::size_t N, typename... Ts> 
void invoke_bar() { 
    auto indices = std::make_index_sequence<sizeof...(Ts) - N>(); 
    invoke_bar_impl<std::tuple<Ts...>, N>(indices); 
}