Variadic template funktioniert nicht wie gedacht
-
@Finnegan
Danke schön nochmal.Mein Originalcode ist sehr umfangreich, der Schnipsel oben befindet sich in einer abstrakten Basisklasse, die Anwendung ist dann im Code einer zweiten Ableitung dieser Basisklasse. Das möchte ich Euch nicht zumuten.
Ich baue morgen mal ein Testprogramm, das nur die wesentlichen Elemente enthält - aber auf meinem Zielsystem. Dann sehe ich wenigstens, ob das Problem an meinem Drumherum oder am Compiler liegt.
-
@Miq Habe mir das gerade nochmal angesehen. Bin mir nicht ganz sicher, warum du die
add
-Spezialisierungen füruint8_t
,uint16_t
, etc. machst, wenn du die daraus gewonnene Information inadd(T)
wieder wegwirfst. Der Ansatz von @wob macht eigentlich auch das was du willst, ohne die Funktion für jeden Typen spezialisieren zu müssen.Der Fehler
...: error: invalid operands of types 'unsigned char*' and 'int' to binary 'operator>>' MM_data[MM_index++] = (v >> (sz << 3)) & 0xFF;
rührte übrigens möglicherweise daher, dass du die Funktion für das Byte-Array alsadd(3, arrayOfBytes)
aufgerufen hast. Das Literal3
hat den Typint
, die Funktionadd(uint16_t count, uint8_t*)
erwartet aber einenuint16_t
, was eine Konvertierung notwendig macht. Durch diese Konvertierung wird diese Funktion bei der Overload-Auflösung jedoch ein schlechterer Kandidat als z.B.add(T v, Args... args)
. Entweder übergibt man hier explizit einenuint16_t
oder man schliesst die Pointer-Argumente beim templatisiertenadd
aus (enable_if
), so dass nur diearrayOfBytes
-Funktion als Kandidat übrig bleibt.Der Ansatz von @wob hat übrigens auch nicht das Problem mit der Endlos-Rekursion, da dein
template <class T> uint16_t add(T v)
als nicht-variadisches Template bevorzugt wird, auch wenn es ansonsten gleichwertig mittemplate <class T, class... Args> add(T v, Args... args)
wäre. Vorher, mittemplate <class... Args> uint16_t add(uint8_t v, Args... args)
war das nicht der Fall, da dieses spezialisierter alstemplate <class T> uint16_t add(T v)
ist (exakter Typ fürv
, ohne Konvertierung).Kurzum, so funkionierts auch - Ansatz von @wob bei dem ich mit
enable_if
Pointer-Argumente ausschliesse, damit in dem Fall derarrayOfBytes
-Overload gewählt wird:uint16_t add(uint8_t *arrayOfBytes, uint16_t count); ... template <class T, class... Args> typename std::enable_if<!std::is_pointer<T>::value, uint16_t>::type add(T v, Args... args) { return add(v) + add(args...); }
Damit das
enable_if
einfacher zu formulieren ist, habe ich auch die Parameter-Reihenfolge desarrayOfBytes
-add
vertauscht. Diese Reihenfolge ist ohnehin die verbreitetere, daher würde ich die sowieso empfehlen.
-
@Finnegan sagte in Variadic template funktioniert nicht wie gedacht:
template <class T, class... Args>
typename std::enable_if<!std::is_pointer<T>::value, uint16_t>::type
add(T v, Args... args) {
return add(v) + add(args...);
}Herzlichen Dank dafür! Die Erklärung mit der Umwandlung nach int leuchtet mir ein, und das Vertauschen der Argumente beim Spezial-add() ist sehr sinnvoll, weil es die template-Ermittlung vereinfacht.
Läuft auch bei mir auf dem ESP32!
-
@Miq sagte in Variadic template funktioniert nicht wie gedacht:
@Finnegan sagte in Variadic template funktioniert nicht wie gedacht:
[...] und das Vertauschen der Argumente beim Spezial-add() ist sehr sinnvoll, weil es die template-Ermittlung vereinfacht.Das kann man hier noch so machen, aber es gibt auch Situationen, in denen sich die Funtionen nicht mehr eindeutig über die Parameter-Typen unterscheiden lassen. Spätestens dann macht es Sinn, einen weiteren Funtionsnamen einzuführen (
add_array
oder sowas). Das ist ne Alternative, wenns noch ärger wird