Ich versuche, in einer std::tuple
variierenden Anzahl von Werten zu speichern , die später als Argumente für einen Aufruf eines Funktionszeigers verwendet werden, der den gespeicherten Typen entspricht.
Ich habe ein vereinfachtes Beispiel erstellt, das das Problem zeigt, das ich nur schwer lösen kann:
#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();
}
Normalerweise std::tuple
würde ich bei Problemen mit oder mit variablen Vorlagen eine andere Vorlage schreiben template <typename Head, typename ...Tail>
, um alle Typen nacheinander rekursiv auszuwerten, aber ich sehe keine Möglichkeit, dies für den Versand eines Funktionsaufrufs zu tun.
Die eigentliche Motivation dafür ist etwas komplexer und meistens nur eine Lernübung. Sie können davon ausgehen, dass ich das Tupel vertraglich von einer anderen Schnittstelle erhalten habe, es kann also nicht geändert werden, aber der Wunsch, es in einen Funktionsaufruf zu entpacken, liegt bei mir. Dies std::bind
schließt aus, dass das zugrunde liegende Problem auf billige Weise umgangen wird.
Was ist eine saubere Methode, um den Anruf mit dem zu versenden std::tuple
, oder eine alternative bessere Methode, um das gleiche Nettoergebnis zu erzielen, indem einige Werte und ein Funktionszeiger bis zu einem beliebigen zukünftigen Punkt gespeichert / weitergeleitet werden?
auto saved = std::bind(f, a, b, c);
... dann später einfach anrufensaved()
?Antworten:
Sie müssen ein Parameterpaket mit Zahlen erstellen und diese entpacken
quelle
struct gens
generische Definition (diejenige, die von einer erweiterten Ableitung desselben erbt ). Ich sehe, dass es schließlich die Spezialisierung mit 0 trifft. Wenn die Stimmung zu Ihnen passt und Sie die freien Zyklen haben, wenn Sie das erweitern können und wie es dafür verwendet wird, wäre ich auf ewig dankbar. Und ich wünschte, ich könnte dies hundertmal abstimmen. Ich hatte mehr Spaß daran, mit Tangenten aus diesem Code zu spielen. Vielen Dank.seq<0, 1, .., N-1>
. Wie es funktioniert :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>
. Der letzte Typ ist spezialisiert und erstelltseq<0, 1, 2, 3, 4>
. Ziemlich kluger Trick.gens
durch:template <int N, int... S> struct gens { typedef typename gens<N-1, N-1, S...>::type type; };
std::integer_sequence<T, N>
und deren Spezialisierung fürstd::size_t
,std::index_sequence<N>
- plus die zugehörigen Hilfsfunktionenstd::make_in(teger|dex)_sequence<>()
und standardisiert wurdestd::index_sequence_for<Ts...>()
. Und in C ++ 17 sind viele andere gute Dinge in die Bibliothek integriert - insbesondere einschließlichstd::apply
undstd::make_from_tuple
, die das Entpacken und Aufrufen von Bits behandeln würdenDie C ++ 17-Lösung ist einfach zu verwenden
std::apply
:Ich hatte nur das Gefühl, dass dies einmal in einer Antwort in diesem Thread angegeben werden sollte (nachdem es bereits in einem der Kommentare erschienen ist).
Die grundlegende C ++ 14-Lösung fehlt in diesem Thread noch. EDIT: Nein, es ist tatsächlich in der Antwort von Walter.
Diese Funktion ist gegeben:
Nennen Sie es mit folgendem Snippet:
Beispiel:
DEMO
quelle
http://coliru.stacked-crooked.com/a/8ea8bcc878efc3cb
std::make_unique
direkt bestehen? Benötigt es eine konkrete Funktionsinstanz? 2. Warum ,std::move(ts)...
wenn wir ändern[](auto... ts)
zu[](auto&&... ts)
?std::make_unique
erwarten ein Tupel, und ein Tupel kann aus einem entpackten Tupel nur über einen anderen Aufruf von erstellt werdenstd::make_tuple
. Dies ist, was ich im Lambda getan habe (obwohl es sehr redundant ist, da Sie das Tupel auch einfach ohne Verwendung in den eindeutigen Zeiger kopieren könnencall
).Dies ist eine vollständig kompilierbare Version von Johannes 'Lösung für Awoodlands Frage, in der Hoffnung, dass sie für jemanden nützlich sein kann. Dies wurde mit einem Snapshot von g ++ 4.7 auf Debian Squeeze getestet.
Man kann die folgende SConstruct-Datei verwenden
Auf meiner Maschine gibt dies
quelle
Hier ist eine C ++ 14-Lösung.
Dies benötigt noch eine Hilfsfunktion (
call_func
). Da dies eine gängige Redewendung ist, sollte der Standard siestd::call
möglicherweise wie bei einer möglichen Implementierung direkt unterstützenDann wird unser verspäteter Versand
quelle
std::call
. Der chaotische Zoointeger_sequence
und die Hilfstypen von C ++ 14 werdenindex_sequence
hier erklärt: en.cppreference.com/w/cpp/utility/integer_sequence Beachten Sie das auffällige Fehlen vonstd::make_index_sequence(Args...)
, weshalb Walter in die klobigere Syntax gezwungen wurdestd::index_sequence_for<Args...>{}
.Dies ist etwas kompliziert zu erreichen (obwohl es möglich ist). Ich rate Ihnen , eine Bibliothek zu verwenden , wo dies bereits umgesetzt wird, nämlich Boost.Fusion (die invoke - Funktion). Als Bonus funktioniert Boost Fusion auch mit C ++ 03-Compilern.
quelle
c ++ 14Lösung. Zunächst einige Utility Boilerplate:
Mit diesen können Sie ein Lambda mit einer Reihe von Ganzzahlen zur Kompilierungszeit aufrufen.
und wir sind fertig.
index_upto
undindex_over
Sie können mit Parameterpaketen arbeiten, ohne neue externe Überladungen generieren zu müssen.Natürlich in c ++ 17 Sie gerade
Nun, wenn uns das gefällt, in c ++ 14 wir können schreiben:
relativ leicht und den Reiniger bekommen c ++ 17 Syntax versandbereit.
ersetzen Sie einfach
notstd
mit ,std
wenn der Compiler - Upgrades und Bob ist dein Onkel.quelle
std::apply
<- Musik in meinen Ohrenindex_upto
und weniger flexibel. ;) Versuchen Sie fordernfunc
mit den Argumenten nach hintenindex_upto
undstd::apply
sind. Zugegeben, wer zum Teufel will eine Funktion von einem Tupel rückwärts aufrufen.std::tuple_size_v
ist C ++ 17, also für die C ++ 14-Lösung, die durchtypename std::tuple_size<foo>::value
value
ist kein Typ. Aber trotzdem behoben.sizeof...(Types)
. Ich mag deine Lösung ohne dietypename
.Wenn ich über das Problem nachdenke, basierend auf der gegebenen Antwort, habe ich einen anderen Weg gefunden, das gleiche Problem zu lösen:
Was erfordert, die Implementierung von
delayed_dispatch()
zu ändern :Dies funktioniert, indem das rekursiv
std::tuple
in ein eigenständiges Parameterpaket konvertiert wird .call_or_recurse
wird als Spezialisierung benötigt, um die Rekursion mit dem realen Aufruf zu beenden, der nur das fertige Parameterpaket entpackt.Ich bin mir nicht sicher, ob dies eine "bessere" Lösung ist, aber es ist eine andere Art, darüber nachzudenken und es zu lösen.
Als weitere alternative Lösung können Sie
enable_if
etwas verwenden, das wohl einfacher ist als meine vorherige Lösung:Die erste Überladung nimmt nur ein weiteres Argument aus dem Tupel und fügt es in ein Parameterpaket ein. Die zweite Überlastung nimmt ein passendes Parameterpaket und führt dann den eigentlichen Aufruf durch, wobei die erste Überlastung in dem einzigen Fall deaktiviert wird, in dem die zweite realisierbar wäre.
quelle
Meine Variation der Lösung von Johannes unter Verwendung von C ++ 14 std :: index_sequence (und Funktionsrückgabetyp als Vorlagenparameter RetT):
quelle