Eine Funktionspointer Liste verwenden, sollte man das tun?



  • Heidiho miteinander,

    ich wollte mich an einem SDL Spiel wagen und habe für mich selbst nach einer Lösung für das Event Management überlegt.

    Mein aktueller Gedankengang ist es, eine Liste von Funktionspointern zu verwenden, in welcher ich laufend hinterlegte Funktionspointer hinzufüge/entferne wie es gerade notwendig ist. Diese Listen werden dann aufgerufen, wenn ein bestimmtes Event auftritt und von hinten aufgerufen. Gibt eine Funktion "Wahr" zurück, hört das weiterlaufen durch die Liste auf. Das soll zumindest das umgesetzte Produkt werden.

    Jetzt möchte ich wissen, ob man das tun sollte. Nachfolgend mal ein kleines Beispiel (wovon ich auch hoffe, dass das überhaupt so richtig ist):

    #include <stdlib.h>
    #include <stdio.h>
    #include <stdbool.h>
    
    struct test
    {
            char name[127];
            bool (*callback[10])( int );
    };
    
    bool test_callback(  int test )
    {
            printf( "From Callback: %i\n", test );
            return true;
    }
    
    bool test_calltwo( int test )
    {
            printf( "From second Callback: %i\n", test + test );
            return true;
    }
    
    int main( int argc, char *argv[argc] ) 
    {
            struct test t = { 0 };
            t.callback[0] = &test_callback;
            (*t.callback[0])( 10 );
            t.callback[1] = &test_calltwo;
            printf( "Received Return Value: %i\n", (*t.callback[1])( 10 ) );
            return 0;
    }
    

    Ich kann das nämlich nicht beurteilen, ob dies ein brauchbarer Weg ist, dass gewollte umzusetzen. Oder ob ich mir lieber einen ganz anderen Ansatz überlegen sollte, Events zu verarbeiten.

    Danke schon einmal für euer Feedback.

    Schönes Wochenende.



  • Das geht auch kürzer:

    int main( int argc, char *argv[] ) 
    {
            struct test t = { 0 };
            t.callback[0] = test_callback;
            t.callback[0]( 10 );
            t.callback[1] = test_calltwo;
            printf( "Received Return Value: %i\n", t.callback[1]( 10 ) );
            return 0;
    }
    

    Selbstverständlich kannst du in allen Lebenslagen Arrays von Funktionszeigern benutzen, das ist gängige Praxis.
    Eine eigene Listenverwaltung musst du dir natürlich auch noch ausdenken.
    Außerdem müssen deine Funktionen auch noch irgendwelche realen Daten verarbeiten, und die übergibt man üblicherweise als Parameter void *data bzw. const void *data. Die höheren Weihen hierbei sind dann auch hier nochmal Funktionszeiger zu verwenden.
    Ich würde statt konkreter Funktionsparameter immer eine leere Parameterliste deklarieren, da kannst du dann (außer ellipsis ...) später alles an Parametern reinpacken - bist somit ziemlich flexibel, musst aber bei der Dereferenzierung selbst aufpassen, da der Compiler hierbei nicht mehr auf Fehler hinweisen kann.

    bool (*callback[10])( );
    


  • Danke dir für deine Rückmeldung @Wutz , dass der Weg durchaus gängig ist . Dann kann ich diesem Ansatz weiter nachgehen.

    Ich war etwas erstaunt, als du die leeren Parameterlisten erwähnt hast. Bisher bin ich davon ausgegangen, dass man die Anzahl der Parameter bei Funktionsaufruf strikt angeben muss. Dabei ist mir auch bei folgendem Beispiel etwas aufgefallen:

    #include <stdlib.h>
    #include <stdio.h>
    #include <stdbool.h>
    
    struct callbacklist
    {
        char eventpattern[127];
        bool (*callback[0])( );
    };
    
    bool testcallback_one( int paramA )
    {
        printf( "CB1: %i\n", paramA );
        return false;
    }
    
    bool testcallback_two( int paramA, int paramB )
    {
        printf( "CB2: Para: %i : %i\n", paramA, paramB );
        printf( "CB2: %i\n", paramA + paramB );
        return false;
    }
    
    int main( int argc, char *argv[argc] )
    {
        struct callbacklist test = { 0 };
        test.callback[0] = testcallback_one;
        test.callback[1] = testcallback_two;
        test.callback[0]( 10, 20 );
        test.callback[1]( 10 );
        return 0;
    }
    

    Obwohl der zweite Aufruf (also bool testcallback_two( int paramA, int paramB ) ) ohne einen zweiten Parameter vorgenommen wird, erhalte ich dennoch die Ausgabe, als hätte ich den Wert 20 übergeben (also wie beim Aufruf zuvor von bool testcallback_one( int paramA )).

    Ist das nicht fehleranfällig auf diese Weise? Es macht den Code flexibler, aber ich erhalte so keine Warnungen, sollte doch mal etwas nicht stimmen und ich vergesse einen Parameter. Dann erhält die Funktion scheinbar zufällige Werte deren Inhalt ich nicht nachvollziehen kann (demnach UB).

    Oder verstehe ich das falsch?



  • @Wutz sagte in Eine Funktionspointer Liste verwenden, sollte man das tun?:

    Außerdem müssen deine Funktionen auch noch irgendwelche realen Daten verarbeiten, und die übergibt man üblicherweise als Parameter void *data bzw. const void *data.

    @fairiestoy sagte in Eine Funktionspointer Liste verwenden, sollte man das tun?:

    test.callback[0]( 10, 20 );
    test.callback[1]( 10 );
    

    Alleine schon die Funktionsaufrufe an sich sind UB. Du müsstest strenggenommen vor einem Call die Funktionszeiger in den richtigentm Zeigertyp zurückcasten.



  • Alleine schon die Funktionsaufrufe an sich sind UB. Du müsstest strenggenommen vor einem Call die Funktionszeiger in den richtigentm Zeigertyp zurückcasten.

    N2310 - §6.5.2.2 Function calls:

    9 If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.

    Wie müsste es denn richtig aussehen?

    test.callback[0]
    

    Dies zeigt ja auf einen Funktionspointer

    bool (*callback)( int )
    

    welches mit der Funktion bool testcallback_one( int paramA ) übereinstimmt, wenn ich einen Zeiger auf diese Funktion verwende.

    Ich kann jedoch nicht ausschließen, dass ich den zitierten Satz aus dem Dokument nicht korrekt verstanden habe. ^^



  • Vergiss bitte was ich gesagt habe, Dein bool (*callback)() ist mit allen Function Pointer Types compatible, die ein bool zurückgeben. Daß beim Aufruf die Parameter in Zahl und Typ passen musst Du sicherstellen.


Log in to reply