J'essaie de stocker dans un std::tuple
nombre variable de valeurs, qui seront plus tard utilisées comme arguments pour un appel à un pointeur de fonction qui correspond aux types stockés.
J'ai créé un exemple simplifié montrant le problème que j'ai du mal à résoudre:
#include <iostream>
#include <tuple>
void f(int a, double b, void* c) {
std::cout << a << ":" << b << ":" << c << std::endl;
}
template <typename ...Args>
struct save_it_for_later {
std::tuple<Args...> params;
void (*func)(Args...);
void delayed_dispatch() {
// How can I "unpack" params to call func?
func(std::get<0>(params), std::get<1>(params), std::get<2>(params));
// But I *really* don't want to write 20 versions of dispatch so I'd rather
// write something like:
func(params...); // Not legal
}
};
int main() {
int a=666;
double b = -1.234;
void *c = NULL;
save_it_for_later<int,double,void*> saved = {
std::tuple<int,double,void*>(a,b,c), f};
saved.delayed_dispatch();
}
Normalement, pour les problèmes impliquant std::tuple
des modèles variadiques, j'écrirais un autre modèle, comme template <typename Head, typename ...Tail>
pour évaluer récursivement tous les types un par un, mais je ne vois pas de moyen de le faire pour envoyer un appel de fonction.
La vraie motivation pour cela est un peu plus complexe et c'est surtout juste un exercice d'apprentissage de toute façon. Vous pouvez supposer que le tuple m'a été remis par contrat à partir d'une autre interface, donc ne peut pas être changé, mais que le désir de le décompresser dans un appel de fonction est le mien. Cela exclut l'utilisation std::bind
comme moyen bon marché de contourner le problème sous-jacent.
Quelle est une façon propre de répartir l'appel à l'aide de la std::tuple
, ou une meilleure façon alternative d'obtenir le même résultat net de stockage / transfert de certaines valeurs et d'un pointeur de fonction jusqu'à un point futur arbitraire?
auto saved = std::bind(f, a, b, c);
... puis appeler plus tardsaved()
?Réponses:
La solution C ++ 17 consiste simplement à utiliser
std::apply
:Je pense que cela devrait être indiqué une fois dans une réponse dans ce fil (après qu'il soit déjà apparu dans l'un des commentaires).
La solution de base C ++ 14 est toujours manquante dans ce thread. EDIT: Non, c'est en fait là dans la réponse de Walter.
Cette fonction est donnée:
Appelez-le avec l'extrait de code suivant:
Exemple:
DEMO
la source
http://coliru.stacked-crooked.com/a/8ea8bcc878efc3cb
std::make_unique
directement? A-t-il besoin d'une instance de fonction concrète? 2. Pourquoistd::move(ts)...
si nous pouvons changer[](auto... ts)
pour[](auto&&... ts)
?std::make_unique
attend un tuple, et un tuple peut être créé à partir d'un tuple décompressé uniquement via un autre appel àstd::make_tuple
. C'est ce que j'ai fait dans le lambda (bien qu'il soit très redondant, car vous pouvez également simplement copier le tuple dans le pointeur unique sans aucune utilisation pourcall
).Vous devez créer un ensemble de paramètres de nombres et les décompresser
la source
struct gens
définition générique (celle qui hérite d'une dérivation étendue de ladite même). Je vois qu'il finit par atteindre la spécialisation avec 0. Si l'ambiance vous convient et que vous avez les cycles de rechange, si vous pouvez développer cela et comment il est utilisé pour cela, je serais éternellement reconnaissant. Et j'aimerais pouvoir voter une centaine de fois. Je me suis plus amusé à jouer avec des tangentes de ce code. Merci.seq<0, 1, .., N-1>
. Comment ça marche:gens<5>: gens<4, 4>: gens<3, 3, 4>: gens<2, 2, 3, 4> : gens<1, 1, 2, 3, 4> : gens<0, 0, 1, 2, 3, 4>
. Le dernier type est spécialisé, la créationseq<0, 1, 2, 3, 4>
. Astuce assez intelligente.gens
par:template <int N, int... S> struct gens { typedef typename gens<N-1, N-1, S...>::type type; };
std::integer_sequence<T, N>
et sa spécialisation pourstd::size_t
,std::index_sequence<N>
- plus leurs fonctions auxiliaires associéesstd::make_in(teger|dex)_sequence<>()
etstd::index_sequence_for<Ts...>()
. Et en C ++ 17, il y a beaucoup d'autres bonnes choses intégrées dans la bibliothèque - en particulier y comprisstd::apply
etstd::make_from_tuple
, qui gèreraient le déballage et l'appel des bitsIl s'agit d'une version compilable complète de la solution de Johannes à la question d'Awoodland, dans l'espoir qu'elle puisse être utile à quelqu'un. Cela a été testé avec un instantané de g ++ 4.7 sur Debian Squeeze.
On peut utiliser le fichier SConstruct suivant
Sur ma machine, cela donne
la source
Voici une solution C ++ 14.
Cela nécessite toujours une fonction d'assistance (
call_func
). Puisqu'il s'agit d'un idiome commun, la norme devrait peut-être le prendre en charge directement commestd::call
pour une implémentation possibleEnsuite, notre envoi retardé devient
la source
std::call
. Le zoo chaotiqueinteger_sequence
etindex_sequence
les types d'assistance de C ++ 14 sont expliqués ici: en.cppreference.com/w/cpp/utility/integer_sequence Remarquez l'absence évidente destd::make_index_sequence(Args...)
, c'est pourquoi Walter a été forcé dans la syntaxe la plus maladroitestd::index_sequence_for<Args...>{}
.C'est un peu compliqué à réaliser (même si c'est possible). Je vous conseille d'utiliser une bibliothèque où cela est déjà implémenté, à savoir Boost.Fusion (la fonction invoke ). En prime, Boost Fusion fonctionne également avec les compilateurs C ++ 03.
la source
c ++ 14Solution. Tout d'abord, certains passe-partout utilitaires:
Ceux-ci vous permettent d'appeler un lambda avec une série d'entiers au moment de la compilation.
et nous avons terminé.
index_upto
etindex_over
vous permet de travailler avec des packs de paramètres sans avoir à générer de nouvelles surcharges externes.Bien sûr, dans c ++ 17 tu viens
Maintenant, si on aime ça, c ++ 14 nous pouvons écrire:
relativement facilement et obtenir le nettoyeur c ++ 17 syntaxe prête à être expédiée.
il suffit de le remplacer
notstd
parstd
lorsque votre compilateur est mis à niveau et que bob est votre oncle.la source
std::apply
<- de la musique à mes oreillesindex_upto
et moins flexible. ;) Essayez d'appelerfunc
avec les arguments en arrière avecindex_upto
etstd::apply
respectivement. Certes, qui diable veut invoquer une fonction d'un tuple vers l'arrière.std::tuple_size_v
est C ++ 17, donc pour la solution C ++ 14 qui devrait être remplacée partypename std::tuple_size<foo>::value
value
n'est pas un type. Mais fixe de toute façon.sizeof...(Types)
. J'aime votre solution sanstypename
.En réfléchissant un peu plus au problème en fonction de la réponse donnée, j'ai trouvé une autre façon de résoudre le même problème:
Ce qui nécessite de changer la mise en œuvre de
delayed_dispatch()
:Cela fonctionne en convertissant récursivement le
std::tuple
en un pack de paramètres à part entière.call_or_recurse
est nécessaire comme spécialisation pour terminer la récursivité avec l'appel réel, qui décompresse simplement le pack de paramètres terminé.Je ne suis pas sûr que ce soit de toute façon une "meilleure" solution, mais c'est une autre façon de penser et de la résoudre.
Comme autre solution alternative que vous pouvez utiliser
enable_if
, pour former quelque chose sans doute plus simple que ma solution précédente:La première surcharge prend simplement un argument de plus du tuple et le place dans un pack de paramètres. La deuxième surcharge prend un pack de paramètres correspondant, puis effectue l'appel réel, la première surcharge étant désactivée dans le seul et unique cas où le second serait viable.
la source
Ma variation de la solution de Johannes utilisant le std :: index_sequence C ++ 14 (et le type de retour de fonction comme paramètre de modèle RetT):
la source