cast eines void* Arg zu 2 verschiedenen Funktionspointern



  • SeppJ schrieb:

    mirrowwinger schrieb:

    Leider benötige ich keine Hinweise zu meinem Programmierstil.

    Das ist wirklich schade, dass du die nicht benötigst. Am Ende könntest du noch was nützliches dabei lernen, also bloß vorsichtig sein!

    Das heißt ich müsste einen "kleinsten gemeinsammen Nenner der beiden Funktionen finden und die beiden danach deklarieren, ala:

    void function(void* Argv)
    

    Sorry SeppJ & Der echte Tim natürlich möchte ich hier etwas lernen. Ist vieleicht sehr hart ausgedrückt gewesen. Das mit dem NULL werde ich nochmal prüfen. Aber etwas geärgert habe ich mich schon, da der Beitrag für mich keinen Inhalt hatte :-(. Wenn ich meine Frage schlecht ausgedrückt habe, versuche ich es nochmal mit einer Abwandlung, welches mein Bestreben vieleicht besser ausdrückt (den Quellcode habe ich jetzt erst gefunden, funktioniert aber nicht 100%).

    typedef int (*fpUserFunction1)(int var1);
    typedef char* (*fpUserFunction2)(char* var2);
    typedef struct struktur{
        ...
        int (*userFunction1)(int Var1);        //  soll durch user gesetzt werden können
        char* (*userFunction2)(char* var2),    //  soll durch user gesetzt werden können
        ...
    }tStruktur, *pStruktur;
    
    /*  Setzen der beiden Funktionen  */
    unsigned int setUserFunction(pStruktur Struktur, unsigned int UserFunctionType, void* Argv){
    //  check if Struktur is initialized
    if (NULL != Struktur){ 
        switch(UserFunctionType){
            case   USER_FUNCTION_1:
                Struktur->userFunction1   =   (fpUserFunction1)Argv;
                break;
            case   USER_FUNCTION_2:
                Struktur->userFunction2   =   (fpUserFunction2)Argv;
                break;
            default:
                //  unbekannter Type
                return 0;
        }
    }
    else
        return 0;
    return 1;
    }
    

    SeppJ schrieb:

    mirrowwinger schrieb:

    Ich werde es mal so versuchen.

    Es braucht gar keinen gemeinsamen Nenner. Alle Funktionspointer sind miteinander kompatibel und können ineinander umgecastet werden, wie es dir gefällt. Sie sind bloß nicht (unbedingt) kompatibel zu "normalen" Zeigern auf Daten. ~Auf jedem gängigen System wird es natürlich auch mit einem void* funktionieren, aber es gibt keinen Grund, es nicht richtig zu machen und irgendeinen Funktionszeiger zu nehmen, so dass es garantiert funktioniert.~

    Mit obrigen Quellcode bekomme ich aber noch die Fehlermeldung:

    error: ISO C forbids conversion of object pointer to function pointer type

    Was anscheinend mit dem Compilerzusatz -pedantic zu tun hat. Gibt es einen Weg dies zu umgehen ohne -pedantic zu löschen?



  • mirrowwinger schrieb:

    Mit obrigen Quellcode bekomme ich aber noch die Fehlermeldung:

    error: ISO C forbids conversion of object pointer to function pointer type

    Kein Wunder, nachdem Du nichts von dem was SeppJ und ich gesagt haben ("Nimm einen beliebigen Funktionszeiger, statt eines void* !") umgesetzt hast, oder? 😉

    #include <stdio.h>
    #include <assert.h>
    
    typedef void (*user_func)();
    typedef int (*user_func1)(int);
    typedef char* (*user_func2)(char*);
    
    struct gadget{
      user_func1 fp1;
      user_func2 fp2;
    };
    
    enum { UF1, UF2 };
    
    void set(struct gadget* g, int id, user_func f) {
      switch(id){
      case (UF1): g->fp1=(user_func1)f; break;
      case (UF2): g->fp2=(user_func2)f; break;
      default:
        assert(0);
      }
    }
    
    int func1(int i) { return i; }
    char* func2(char* s) { return s; }
    
    int main(void) {
      struct gadget g;
      set(&g, UF1, (user_func)func1);
      set(&g, UF2, (user_func)func2);
      printf("%s%i\n", (*g.fp2)("The Answer is "), (*g.fp1)(42));
    }
    


  • Das ist alles UB.



  • Wutz schrieb:

    Das ist alles UB.

    Weshalb?



  • Furble Wurble schrieb:

    mirrowwinger schrieb:

    Mit obrigen Quellcode bekomme ich aber noch die Fehlermeldung:

    error: ISO C forbids conversion of object pointer to function pointer type

    Kein Wunder, nachdem Du nichts von dem was SeppJ und ich gesagt haben ("Nimm einen beliebigen Funktionszeiger, statt eines void* !") umgesetzt hast, oder? 😉

    #include <stdio.h>
    #include <assert.h>
    
    typedef void (*user_func)();
    typedef int (*user_func1)(int);
    typedef char* (*user_func2)(char*);
    
    struct gadget{
      user_func1 fp1;
      user_func2 fp2;
    };
    
    enum { UF1, UF2 };
    
    void set(struct gadget* g, int id, user_func f) {
      switch(id){
      case (UF1): g->fp1=(user_func1)f; break;
      case (UF2): g->fp2=(user_func2)f; break;
      default:
        assert(0);
      }
    }
    
    int func1(int i) { return i; }
    char* func2(char* s) { return s; }
    
    int main(void) {
      struct gadget g;
      set(&g, UF1, (user_func)func1);
      set(&g, UF2, (user_func)func2);
      printf("%s%i\n", (*g.fp2)("The Answer is "), (*g.fp1)(42));
    }
    

    Gemäß Quelle soll mein dargestellter Ansatz "angeblich" (also ich habe es noch nicht getestet) ohne den Compiler-Zusatz -pedantic funktionieren.



  • SeppJ schrieb:

    Alle Funktionspointer sind miteinander kompatibel und können ineinander umgecastet werden, wie es dir gefällt. Sie sind bloß nicht (unbedingt) kompatibel zu "normalen" Zeigern auf Daten.

    Funktionszeiger können gecastet werden, das ist richtig.
    Dass alle Funktionspointer miteinander kompatibel sind, ist falsch und eine gefährliche Verkürzung der Aussage des Standards hierzu, die nämlich noch weiter geht.
    ... UB liegt vor, wenn der (einem vorigen Cast folgende) Aufruf einer Funktion über einen Zeiger vom Typ erfolgt, der inkompatibel zum aktuellen (d.h. definierten) Funktionstyp ist.
    Eine weitere Einschränkung liegt für Funktionen mit variablen Parameterlisten vor, die sind niemals zu irgendwas kompatibel, insbesondere nicht zu einem 'generischen' Funktionszeiger.
    Fazit:
    Ein Funktionscast ist immer zu vermeiden, weil ggü. einem Datenzeiger-Cast das Verhalten bei einer folgenden Dereferenzierung (bei Funktionszeigern also dem Funktionsaufruf) noch viel weniger durchschaubar ist, Fehler also schwerer reproduzierbar/analysierbar (z.B. im Debugger) sind.

    http://ideone.com/bnHmTU

    typedef void*(*PtrAllgemein)();
    
    /* statt int bla(int) : */
    void* bla(int i){printf("%d",i);return (void*)i;}
    
    /* statt char *foo(char *) : */
    void* foo(char *s){puts(s);return 0;}
    
    int main() {
    	int i;
    	enum {BLA,FOO};
    	PtrAllgemein p[] = {bla,foo};
    
    	p[BLA](2);
    	p[FOO]("hello");
    
    	/* für die Return-Typen der Funktionen sollte man dann aber nur Zeiger verwenden,
    	evtl. würden noch integer Typen gehen, die dann aber (sowohl in der Funktion als auch beim Aufruf) gecastet werden müssen: */
    
    	i = (int)p[BLA](1234);
    	printf("%d",i);
    
    	return 0;
    }
    

    Man sieht also, man kommt völlig ohne Funktionszeigercasts aus und hat trotzdem kein UB.
    --> also: liebe Kinder,
    - nie Funktionen casten!
    - keine Funktionen mit variablen Parametern über Zeiger verwenden
    - der "generische" Funktionszeiger sollte immer einen kompatiblen Rückgabetyp zu den verwendeten Funktionen besitzen, zur Not also mehrere Funktionszeigertypen verwenden, bzw. wie oben gezeigt, nur "sichere" Casts bei Objekttypen der Rückgabewerte verwenden



  • mirrowwinger schrieb:

    * folgender Quellcode ist fehlerfrei kompilierbar!!!

    lol

    Die Leute begreifen einfach nicht, dass solche Aussagen einfach nur naiv sind.
    Sie machen sich vom Compilerbauer abhängig, der je nach Gemütslage für Unsinns- bzw. UB-Code trotzdem irgendwas (aus seiner Sicht) halbwegs Brauchbares zusammenbastelt, nur dass es irgendwie weitergeht, damit der Programmierer nicht beleidigt wegen eines Compilerabbruchs ist.

    --> ein fehlerfreier Compilerdurchlauf sagt überhaupt nichts aus über die Fehlerfreiheit/Portabilität/Plattformunabhängigkeit des Codes aus (zumal der Warning-Level dabei auch noch eine Rolle spielt) und schon gar nicht über fachliche Fehler im Code (was einfacher einleuchten sollte)



  • @Wutz: Ich sehe da jetzt keinen Vorteil in Deiner Lösung. Insbesondere kann ich durchaus schreiben i = (int)p[BLA]("Hello", 0.1, bla, foo); . Da ist die Lösung mit dem casten auch nicht unsicherer.
    Ich bin mir auch nicht sicher, wie Du die Funktionen des OP, die nicht dn Rückgabetyp void* haben umbiegen willst, damit Sie in Dein Array p[] passen?

    Diese "generischen Funktionszeiger" mit all der casterei ist sicherlich nicht hübsch, aber nicht von vorneherein UB.



  • Du hast meinen Beitrag nicht verstanden.
    Dein gesamter Code ist UB.
    Meine "Lösung" für Nicht-Zeiger-Rückgabetypen habe ich gezeigt.



  • Machen wir doch mal Butter bei die Fische:

    c11 6.3.2.3/8 schrieb:

    A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer.
    If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

    Das passiert aber nicht in meinem Beispiel. Woran störst Du Dich also?

    Wutz schrieb:

    Meine "Lösung" für Nicht-Zeiger-Rückgabetypen habe ich gezeigt.

    Ja stimmt. Den Rückgabetyp in einen Zeiger casten.


Anmelden zum Antworten