functor_traits und lambda_traits



  • Du kannst keine Funktionspointer implizit nach void* konvertieren.
    Wieso so check-Dinger nicht gehen, kannste gut erkennen indem du den default-Fall deaktivierst.
    Übrigens: int kann genauso groß sein wie char.
    Nimm besser char und char[2] oder so.


  • Mod

    Arcoth schrieb:

    Wie wärs mit

    template <void*>
        struct Nothing
        {
            typedef char type;
        };
     
        template <typename U>
        static typename Nothing<&U::operator()>::type check(int);
     
        template <typename>
        static int check(char);
     
    public:
     
        static const bool is_functor = sizeof(check<T>(0)) == 1;
    

    Weder Funktionszeiger noch Zeiger auf Member sind implizit in void* konvertierbar.
    Schnell hingeschrieben könnte es so funktionieren. Kann man noch etwas vereinfachen.


  • Mod

    camper schrieb:

    Schnell hingeschrieben könnte es so funktionieren.

    Völlig irre! Warum sind die Rückgabetypen Referenzen?

    Ich schlage als korrigierte Version

    template<typename T>
    class test
    {
    	template <int>
    	struct Nothing {typedef char type;};
    
    	template <typename U>
    	static typename Nothing<sizeof(&U::operator())>::type check(int);
    
    	template <typename>
    	static int check(char);
    
    public:
    
    	static const bool is_functor = sizeof(check<T>(0)) == 1;
    };
    

    vor.



  • template<typename T>
    class test
    {
    	template <int>//int rein => char raus.
    	struct Nothing {typedef char type;};
    
    	template <typename U>//int rein => char raus.
    	static typename Nothing<sizeof(&U::operator())>::type check(int);
    
    	template <typename>//char rein => int raus.
    	static int check(char);
    
    public:
    
    	static const bool is_functor = sizeof(check<T>(0)) == 1;
    };
    


  • volkard schrieb:

    template<typename T>
    class test
    {
    	template <int>//int rein => char raus.
    	struct Nothing {typedef char type;};
    
    	template <typename U>//int rein => char raus.
    	static typename Nothing<sizeof(&U::operator())>::type check(int);
    
    	template <typename>//char rein => int raus.
    	static int check(char);
    
    public:
    
    	static const bool is_functor = sizeof(check<T>(0)) == 1;
    };
    

    ?
    Das ist Absicht.



  • Nathan schrieb:

    Das ist Absicht.

    Ist es notwendig?



  • volkard schrieb:

    Nathan schrieb:

    Das ist Absicht.

    Ist es notwendig?

    Ja.
    Der unterschiedlich Rückgabewert weil sonst sizeof auf den Rückgabewert nicht funktioniert um die ausgewählte Funktion zu bestimmen und der unterschiedliche Paramter, damit die char-Version wenn man sie mit einem int aufruft, geringere Priorität wegen der Konvertierung nach char hat, als die andere Funktion, die man direkt mit einem int aufrufen kann. Ansonsten hätte man eine Ambiguität.



  • Nathan schrieb:

    volkard schrieb:

    Nathan schrieb:

    Das ist Absicht.

    Ist es notwendig?

    Ja.
    Der unterschiedlich Rückgabewert weil sonst sizeof auf den Rückgabewert nicht funktioniert um die ausgewählte Funktion zu bestimmen und der unterschiedliche Paramter, damit die char-Version wenn man sie mit einem int aufruft, geringere Priorität wegen der Konvertierung nach char hat, als die andere Funktion, die man direkt mit einem int aufrufen kann. Ansonsten hätte man eine Ambiguität.

    Danke, daß Du jetzt neben Arcoth auch mich für einen Vollidionten hältst.
    Und jetzt antworte nochmal gescheit, warum Du zweimal int/char vertauscht, mir scheint der Doppeltausch nichts zu bewirken, außer die Lesbarkeit zu senken.



  • volkard schrieb:

    Nathan schrieb:

    volkard schrieb:

    Nathan schrieb:

    Das ist Absicht.

    Ist es notwendig?

    Ja.
    Der unterschiedlich Rückgabewert weil sonst sizeof auf den Rückgabewert nicht funktioniert um die ausgewählte Funktion zu bestimmen und der unterschiedliche Paramter, damit die char-Version wenn man sie mit einem int aufruft, geringere Priorität wegen der Konvertierung nach char hat, als die andere Funktion, die man direkt mit einem int aufrufen kann. Ansonsten hätte man eine Ambiguität.

    Danke, daß Du jetzt neben Arcoth auch mich für einen Vollidionten hältst.

    Äh, moment.
    Arcoth halte ich nicht für einen Vollidioten, ich dachte der fehlerhafte Code den Abrakadabra zitiert hatte, stammte von ihm, als ich meinen Beitrag geschrieben habe, da ich den von ihm nur überflogen hatte. Das Arcoth den Fehler gemacht hat, fiel mir nicht auf, weswegen ich meine Antwort halt so formuliert.
    Und dich halte ich auch nicht für einen Vollidioten. Ich hatte mich schon gewundert, dass du das gefragt hast*, habs aber normal beantwortet, weil ich dachte, dass du auf etwas hinauswillst, was du ja scheinbar auch wolltest. Ich hatte gerechnet, dass du mich jetzt total bloßstellst, weil ich irgendetwas nicht wusste.

    Und jetzt antworte nochmal gescheit, warum Du zweimal int/char vertauscht, mir scheint der Doppeltausch nichts zu bewirken, außer die Lesbarkeit zu senken.

    Der Code ist von Arcoth, nicht von mir.
    Und was meinst du mit "Doppeltausch"?

    *Siehe mein Fragezeichen, bevor ich "Das ist Absicht" schrieb.



  • Sorry, ambiq formuliert, gemein war "daß nach Arcoth auch Du mich für".

    Nathan schrieb:

    weil ich dachte, dass du auf etwas hinauswillst,

    Mich hat's verwirrt, daß int/char zweimal vertauscht werden und dachte, wenn man das vermeiden würde, müßte man nur noch halb so viel sich im Kopf merken, um zu verstehen, wie der Code geht. Vielleicht bin ich noch nicht richtig wach heute, aber irgendwie scheint mir der "Doppeltausch" ungünstig zu sein.

    template<typename T>
    class test
    {
        template <int>//int rein => int raus.
        struct Nothing {typedef int type;};
    
        template <typename U>//int rein => int raus.
        static typename Nothing<sizeof(&U::operator())>::type check(int);
    
        template <typename>//char rein => char raus.
        static char check(char);
    
    public:
    
        static const bool is_functor = sizeof(check<T>(0)) != 1;
    };
    


  • volkard schrieb:

    Sorry, ambiq formuliert, gemein war "daß nach Arcoth auch Du mich für".

    Nathan schrieb:

    weil ich dachte, dass du auf etwas hinauswillst,

    Mich hat's verwirrt, daß int/char zweimal vertauscht werden und dachte, wenn man das vermeiden würde, müßte man nur noch halb so viel sich im Kopf merken, um zu verstehen, wie der Code geht. Vielleicht bin ich noch nicht richtig wach heute, aber irgendwie scheint mir der "Doppeltausch" ungünstig zu sein.

    template<typename T>
    class test
    {
        template <int>//int rein => int raus.
        struct Nothing {typedef int type;};
    
        template <typename U>//int rein => int raus.
        static typename Nothing<sizeof(&U::operator())>::type check(int);
    
        template <typename>//char rein => char raus.
        static char check(char);
    
    public:
    
        static const bool is_functor = sizeof(check<T>(0)) != 1;
    };
    

    Ach so, das meinst du. Die int Version sollte auch int nehmen und die char Version char.
    Ja, da stimme ich dir zu, ich habe das Problem bei meinem generischem "Ausdruck gültig?" folgendermaßen umgangen:

    template <typename T, typename Expr>
    class expression_valid
    {
        // char und int haben keinerlei semantische Bedeutung und können außerdem noch gleich groß sein
        using matched_t     = char(&)[1];
        using unmatched_t   = char(&)[2];
    
        template <class T2>
        static matched_t checker(decltype(Expr::template check<T2>())) noexcept;
    
        // ellipsis als default, das ist aktuell noch der einzig anehmbare Verwendungszweck in C++ und außerdem garantiert das schlechteste Match
        template <class T2>
        static unmatched_t checker(...) noexcept;
    
    public:
        // weitaus mehr Semantik als == 1
        // besser wäre sogar noch is_same, aber gut
        static constexpr auto value = sizeof(checker<T>(0)) == sizeof(matched_t);
    };
    

  • Mod

    @volkard Niemand hält dich für einen Idioten.

    Vielleicht bin ich noch nicht richtig wach heute, aber irgendwie scheint mir der "Doppeltausch" ungünstig zu sein.

    Das ist mir direkt aufgefallen nachdem ich den Code geschrieben habe - die Tatsache dass das irgendjemand benörgeln wird war mir auch sofort bewusst, aber ich war einfach zu faul es zu korrigieren.

    @Nathan Für matched_t & co. etwas benutze ich das oder ähnliches. Fasst du so etwas nicht zusammen?
    Und war es wirklich nötig checker als noexcept zu markieren?

    Endlich ist mir bei Nathans Version aufgefallen warum die Rückgabetypen Referenzen sind - wären ja sonst beides Zeiger.



  • Arcoth schrieb:

    @Nathan Für matched_t & co. etwas benutze ich das oder ähnliches. Fasst du so etwas nicht zusammen?

    Ich brauchte zu dem Zeitpunkt nur zwei einfache Typedefs unterschiedlicher Größe, da kam ich gar nicht auf die Idee das zu generalisieren.
    Dein rank Ding ist aber nice.

    Und war es wirklich nötig checker als noexcept zu markieren?

    Gewohnheit. Ich war damals (vor 6 Monaten oder so) in der Phase, wo noexcept mein Default für Funktionen war.
    Für neueren Code hab ich meine Exceptionspolicies geändert und somit auch mein noexcept. Ich nutz das jetzt nur aus semantischen Gründen, für Funktionen die nie schief gehen können (Ein operator[] ist bspw. nicht noexcept*, selbst wenn er nicht den Index checkt, man kann einen Segfault kriegen, d.h. die Funktion failen).

    Endlich ist mir bei Nathans Version aufgefallen warum die Rückgabetypen Referenzen sind - wären ja sonst beides Zeiger.

    Da hast du mir was voraus. Ich musste beim Copy-Pasten erst einmal ewig überlegen, wieso Referenzen.

    *Genau genommen ist der idR ein spezielles NOFAIL, was "geht nur schief, wenn Programmierer dumm ist" bedeutet und je nachdem ob Logikfehler gecheckt werden noexcept oder nicht ist, aber darum gehts hier nicht.


  • Mod

    Genau genommen ist der idR ein spezielles NOFAIL, was "geht nur schief, wenn Programmierer dumm ist" bedeutet

    😃 😃

    man kann einen Segfault kriegen,

    Du tust da aber entsprechende Assertions rein, richtig?



  • Arcoth schrieb:

    man kann einen Segfault kriegen,

    Du tust da aber entsprechende Assertions rein, richtig?

    Ja, ich habe ein spezielles CHECK-Makro, das ist quasi wie ein assert, nur mit Priorität (implementation-Fehler muss man jetzt nicht immer überprüfen...) und kann bei Bedarf auch Exceptions werfen. Deswegen ist der NOFAIL, wenn checks deaktiviert sind, also auch noexcept um ggf. die Optimierung zu kriegen.


Anmelden zum Antworten