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ür uint8_t, uint16_t, etc. machst, wenn du die daraus gewonnene Information in add(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 als add(3, arrayOfBytes) aufgerufen hast. Das Literal 3 hat den Typ int, die Funktion add(uint16_t count, uint8_t*) erwartet aber einen uint16_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 einen uint16_t oder man schliesst die Pointer-Argumente beim templatisierten add aus (enable_if), so dass nur die arrayOfBytes-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 mit template <class T, class... Args> add(T v, Args... args) wäre. Vorher, mit template <class... Args> uint16_t add(uint8_t v, Args... args) war das nicht der Fall, da dieses spezialisierter als template <class T> uint16_t add(T v) ist (exakter Typ für v, ohne Konvertierung).

    Kurzum, so funkionierts auch - Ansatz von @wob bei dem ich mit enable_if Pointer-Argumente ausschliesse, damit in dem Fall der arrayOfBytes-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...);
    }
    

    https://ideone.com/uRScg6

    Damit das enable_if einfacher zu formulieren ist, habe ich auch die Parameter-Reihenfolge des arrayOfBytes-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 😉


Anmelden zum Antworten