C++Guns – RoboBlog

02.06.2018

C++ Guns: convert tuple to parameter pack

Filed under: Allgemein — Tags: — Thomas @ 10:06

Part1: print std::array with std:integer_sequence
Part2: convert tuple to parameter pack
Part 3: print std::array with std::apply and fold
Part 4: fold over std::tuple und erzeugten Assembler Code
Part 5: fold over std::tuple of std::vector of Types ...
Part 6: apply generic lambda to tuple
Part 7: Play with std::tuple and std::apply

Guckst du hier https://en.cppreference.com/w/cpp/language/parameter_pack
und hier https://en.cppreference.com/w/cpp/language/fold
und da https://stackoverflow.com/questions/47216057/how-to-write-a-fold-sum-function-for-c-tuple

parameter pack zu std::tuple ist einfach

template<typename...Ts>
auto func(const Ts&...args) {
    return std::tuple(args...);
}

auto func2() {
    return func(1, 2.0, "test");
}

https://stackoverflow.com/questions/47216057/how-to-write-a-fold-sum-function-for-c-tuple

Der template Type Ts besteht aus den drei einzelnen Typen int, double und const char*. Die Variable args... hält entsprechend die drei Werte. Das sieht man an den drei Punkten "..." bei der Type und Funktions Argument Definition. Die sagen aus, dass hier noch mehr kommt. Die drei Punkte beim erstellen vom tuple packen den parameter pack aus. Sinngemäß wird der Ausdruck umgewandelt zu

return std::tuple<int,double,const char>{arg1, arg2, arg3};

Andersherum, also vom tuple zum parameter pack ist nicht so einfach. Ein Weg führt über std::integer_sequence wie ich es im anderen Post gezeigt habe. Aber das ist wieder so viel Template gedöns und man braucht zwei Funktionen. Ein anderer Weg führt über C++17 std::apply und fold, wie in dem stackoverflow Post gezeigt. Man benötigt zeitgleich aber eine Funktion, welche beim fold auf die Argumente angewandt werden kann. Das ganze bewegt sich weg von dem WIE etwas passiert, hin zu dem WAS passieren soll. Dem bin ich positiv gegenübergestellt.

auto t = std::make_tuple( 1, 2.0 );
auto func = []( auto... v ){ return ( v + ... ); };
auto sum = std::apply(func, t);

In std::apply steckt eine std::integer_sequence drin, welche aus dem tuple erstellt und der Funktion übergeben wird. damit haben wir wieder ein parameter pack. Mittels lambda Funktion spart man sich auch wieder die Templates. Letztendlich müssen wir mit dem parameter pack irgendetwas tun, also wird der plus operator mittels fold aufgerufen.

Das ist schon eine ganz andere Art zu Programmieren, so ganz ohne explizite Schleifen. Aber ich finde es gut. Verhindert Fehler und der Compiler kann mehr tun.
Zum Abschluss noch ein kurzer Spaß. Was passiert, wenn man ein tuple in ein parameter pack umwandelt und das zurück in ein tuple? Und wie sieht der Assembercode dazu aus? Probieren wir es aus!

auto func(const std::tuple<int,double,const char*>& t) {
    auto f = [](const auto&...v){ return std::tuple{v...}; };
    return std::apply(f, t);   
}
func(std::tuple<int, double, char const*> const&):
  movq %rdi, %rax
  movq (%rsi), %rdx
  movq %rdx, (%rdi)
  movsd 8(%rsi), %xmm0
  movsd %xmm0, 8(%rdi)
  movl 16(%rsi), %edx
  movl %edx, 16(%rdi)
  ret

Nun es passiert nichts weiter ;) Es gibt Sieben Assembler Instruktionen. Im Prinzip werden die Argumente nur kopiert. Jede Kopieraktion braucht zwei Instruktionen, da nicht von Speicher zu Speicher kopiert werden kann. Nur von Speicher zu Register und von Register zu Speicher. Die Argumente stehen im rsi Register. Wenn mich nicht alles täuscht, wird erst der const char Pointer kopiert. Also ein quadword mit 64 Byte. Danach kommt das double, erkennt man am xmm0 Register. Und zuletzt das int mit einer movl Instruktion.

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress