Übergeben eines Tupels an eine variadische Mixin-Klasse
-
21-12-2019 - |
Frage
Ich versuche, eine Reihe von Parametern wie folgt an zwei verschiedene Mixin-Klassen weiterzuleiten:
template <typename... Checkers>
class Checker : public Checkers... {
public:
template<typename... Args>
Checker(Args&&... args) : Checkers(std::forward<Args>(args))... { }
};
template <typename... Handlers>
class Handler : public Handlers... {
public:
template <typename... Args>
Handler(Args&&... args) : Handlers(std::forward<Args>(args))... { }
};
template <typename C, typename H>
class Tester : public C, H {
public:
template <typename... ArgC, typename... ArgH>
Tester(std::tuple<ArgC...>&& argc, ArgH&&... argh) : C(argc), H(argh...) {
}
};
Checker und Handler sind zwei verschiedene Sammlungen von Mixin-Klassen mit unterschiedlichen Anforderungen für jede, aber gemeinsamen Anforderungen für alle Mitglieder.Mir ist klar, dass ich im Tester keine doppelte variadische Konstruktion durchführen kann (der Compiler kann nicht ableiten, wo die Argumente aufgeteilt werden sollen, also übergibt er sie alle an den Handler und keine an den Prüfer), also übergebe ich die Prüferargumente in einem Tupel und die Handlerargumente in eine variadische Liste.Das Problem ist, dass der Konstruktor von Checker dafür verantwortlich ist, diese Parameter an seine Basen weiterzuleiten.Handler kann dies tun, weil der Konstruktor des Handlers eine variadische Liste ist, aber Checker erhält ein Tupel, aber Sie können nicht für jeden die Elemente eines Tupels weiterleiten, wie forward eine variadische Parameterliste.
Jeder Rat wäre sehr dankbar.Danke.
Zusätzlich
Eine Lösung wäre entweder a) auspacken argc
in Checker
variadischer Konstruktor oder b) Make Checker
der Konstruktor nimmt ein Tupel und leitet dann irgendwie jedes Element dieses Tupels an jedes von weiter Checker
mixin Basen, Checkers...
.Mir ist klar std::pair
hat einen Trick, um ein Tupel als feste Argumentliste an seine Typen weiterzuleiten, z. B. eine 3-parametrische std::vector
konstruktor mit dem std::piecewise_construct
geben Sie eine Markierung ein, um sie über das Auspacken zu informieren, aber ich sehe nicht, wie dies hier angewendet werden kann.Ich habe mir die Implementierung von GCC 4.8.1 angesehen std::pair
mit std::piecewise_construct
und konnte es nicht herausfinden.Ich habe einige der älteren Bücher vor C ++ 11 über Template-Metaprogrammierung gelesen (Modernes C ++ - Design und C ++ -Vorlagen-Metaprogrammierung, zum Beispiel), aber ich bin hier jetzt ratlos, da es einen Standard gibt und ich versuche, Boost und Loki zu vermeiden.
Zusätzlich
Muss mindestens entsprechen GCC 4.7.2.Die Lösung, die ich gefunden habe, basiert auf std::pair
benötigt Konstruktorvererbung, die erst verfügbar wurde GCC 4.8.1 was auf meinem Build-System nicht unterstützt wird.
Zusätzlich
Obwohl GCC 4.6.3 unterstützung wäre nett, Delegierte Konstrukteure wurden in 4.7.2 hinzugefügt, damit ich Zugriff auf diese Sprachfunktion haben sollte.
Lösung
http://cpptruths.blogspot.fr/2012/06/perfect-forwarding-of-parameter-groups.html
Davidbrczs Lösung, wie in seinem obigen Blog angegeben, reichte aus, um mein Rätsel zu lösen.Die Lösung ist ziemlich komplex, daher leite ich Sie auf seine Seite weiter, aber die Grundidee besteht darin, im laufenden Betrieb ein numerisches Indextupel à la std::maketuple(0, 1, 2, ...), wobei das Tupel jeden der Indizes der verschiedenen Mitglieder des Tupels enthält, die Sie aufzählen müssen.Dann benutzt du einfach:
M(std::forward<ArgM>(std::get<IdxM>(argm))...)
Für M entweder als C oder H im obigen Beispiel und ArgM die Argumente für M und IdxM das gleich große Tupel von Indizes.Da die Listen gleich lang sind, wird die Liste im Tandem in Parameter gerollt und das Tupel wird entpackt.
Die Einschränkung besteht darin, dass Sie möchten, dass der komplizierte Schritt zum Erstellen des Indextupels als Implementierungsdetail ausgeblendet wird. Sie müssen die Konstruktordelegierung so verwenden, dass der öffentliche Konstruktor die 2 Tupel verwendet, die dann an den privaten Konstruktor delegiert werden, der 2 Werttupel und 2 Werte annimmt Indextupel.GCC 4.7.2 unterstützt delegierte Konstruktoren, 4.6.3 jedoch nicht.
Um dies zu umgehen, müssen Sie den 4-Parameter-Konstruktor (2 Tupel von Werten, 2 Tupel von Indizes) öffentlich machen und dann habe ich ein Makro geschrieben, um die Indextupelparameter auszufüllen:
#if __GNUC__ < 4 || __GNUC_MINOR__ <= 6
#define ZEROPARAM , detail::make_indices<>::type()
#define ONEPARAM , detail::make_indices<int>::type()
#define TWOPARAM , detail::make_indices<int, int>::type()
#define THREEPARAM , detail::make_indices<int, int, int>::type()
#define FOURPARAM , detail::make_indices<int, int, int, int>::type()
#define FIVEPARAM , detail::make_indices<int, int, int, int, int>::type()
#define SIXPARAM , detail::make_indices<int, int, int, int, int, int>::type()
#define SEVENPARAM , detail::make_indices<int, int, int, int, int, int, int>::type()
#define EIGHTPARAM , detail::make_indices<int, int, int, int, int, int, int, int>::type()
#define NINEPARAM , detail::make_indices<int, int, int, int, int, int, int, int, int>::type()
#define TENPARAM , detail::make_indices<int, int, int, int, int, int, int, int, int, int>::type()
#else // __GNUC__ < 4 || __GNUC_MINOR__ <= 6
#define ZEROPARAM
#define ONEPARAM
#define TWOPARAM
#define THREEPARAM
#define FOURPARAM
#define FIVEPARAM
#define SIXPARAM
#define SEVENPARAM
#define EIGHTPARAM
#define NINEPARAM
#define TENPARAM
#endif // __GNUC__ < 4 || __GNUC_MINOR__ <= 6
Und fügen Sie dann nach dem Aufbau des angegebenen Testers das entsprechende Makro hinzu, zumindest solange noch Leute auf GCC 4.6.3 sind, während ich daran arbeite, alle auf mindestens 4.7.2 und vorzugsweise 4.8.1 zu bringen.:)
Ich wünschte, ich könnte Davidbrcz Anerkennung für die Lösung zollen, aber zumindest könnte dies für Leute hilfreich sein, die vor einem ähnlichen Problem stehen, um seine Lösung in ihrem speziellen Fall anzuwenden.Die Hauptsache ist, seine make_indices-Vorlagenklasse zu kopieren, um die eigentliche Arbeit zu erledigen;der Rest ist ein Kuchenspaziergang!