Juhuuu, mal wieder Funktionszeiger !



  • Hi!
    Einem void* Zeiger kann man ja bekanntlich zur Laufzeit verschiedene Datentypen zuweisen.
    Geht so etwas auch mit Funktionen ? Oder bleibe ich da vor Unionen nicht verschont ?
    Ich möchte gern einem Zeiger zur Laufzeit verschiedene Funktionen mit zuweisen.
    Möglicherweise geht das aber auch gar nicht. 😃
    Hab sowas jedenfalls noch nicht gesehen.
    Also ich habe mir so etwas in der Art gedacht( was leider nicht funzt):

    void *fp;
    
    	fp = pow;    // warning nonstandard extension, function/data pointer conversion in expression
    	fp(1.0,2.0); // error 'fp' : not a function
    	fp = abs;    // warning nonstandard extension, function/data pointer conversion in expression
    	fp(-1);		 // error 'fp' : not a function
    	// auch mit überredungskünsten wie casten ist da nix zu machen
    	(double(*)(double,double))fp(1.0,2.0); // error 'fp' : not a function
    	(int   (*)(int)) fp(-1);		// error 'fp' : not a function
    

    So etwas wie einen

    (...)(*magic_funct_ptr)(...)
    

    gibts wohl nicht 🤡

    Bleiben nur noch Unionen, ne.
    gruß
    p.n. 🙂



  • ...oder ein void* Parameter, der auf eine struct mit Parametern zeigt.

    greetz, Swordfish



  • Das primitivste Beispiel sieht in etwa so aus:

    // Funktionskette
    
    struct registered_function
    {
    	void (*functionpointer) (void);
    	struct registered_function *next_function;
    };
    
    unsigned int registered_functions = 0;
    static struct registered_function  *first_function = NULL;
    static struct registered_function  *last_function = NULL;
    
    // -------------------------------------------------------------------------------------------
    // Funktion eintragen
    unsigned int AddFunction( void (*function) (void) )
    {
    struct registered_function *new_function = malloc(sizeof(struct registered_function));
    	if (new_function)
    		{
    			new_function->functionpointer = function;
    			new_function->next_function = NULL;
    
    			if (registered_functions) last_function->next_function = new_function;
    			else first_function = new_function;
    			last_function = new_function;
    			registered_functions++;
    			return registered_functions;
    		}
    	else return 0;
    }
    
    // -------------------------------------------------------------------------------------------
    // Funktion irgendwann ausführen
    void DoFunctions(void)
    {
    struct registered_function *counter;
    
    // void (*ptr) (void); Kette von void- Funktionen
    	if (registered_functions)
    	{
    		counter = first_function;
    		while (counter)		
    		{
    			(*counter->functionpointer)();
    			counter = counter->next_function;
    		}
    	}
    }
    

    Wenn Du in der Struct noch einen Union- Block reinsetzt, der die Argumente mitträgt und den Funktionspointer auf void * castest (folglich auch in der struct statt des Funktionspointers einen void* anlegst), hast Du's fast. Dann kannst Du beliebige Funktionspointer auf die eigene struct loslassen, aber Vorsicht ⚠ Ein klitzekleiner Fehler und das Konstrukt geht komplett krachen!


  • Mod

    pointer n00b schrieb:

    void *fp;
    
    	fp = pow;    // warning nonstandard extension, function/data pointer conversion in expression
    	fp(1.0,2.0); // error 'fp' : not a function
    	fp = abs;    // warning nonstandard extension, function/data pointer conversion in expression
    	fp(-1);		 // error 'fp' : not a function
    	// auch mit überredungskünsten wie casten ist da nix zu machen
    	(double(*)(double,double))fp(1.0,2.0); // error 'fp' : not a function
    	(int   (*)(int)) fp(-1);		// error 'fp' : not a function
    

    Alles nur eine Frage des Vorrangs von Operatoren:

    ((double(*)(double,double))fp)(1.0,2.0);
    	((int(*)(int))fp)(-1);
    

    Natürlich abgesehen von den prinzipiellen Problemen dieser Konvertierung.



  • @pointercrash()
    so in der art wird es tatsächlich ablaufen, bloss das ich mir einen strkuturblock am stück hole und mit einem einfachen index-operator drüber wandere.

    camper schrieb:

    Alles nur eine Frage des Vorrangs von Operatoren:

    ((double(*)(double,double))fp)(1.0,2.0);
    ((int(*)(int))fp)(-1);
    

    Natürlich abgesehen von den prinzipiellen Problemen dieser Konvertierung.

    der compiler meckert zwar immer noch:

    warning: nonstandard extension, function/data pointer conversion in expression
    warning: 'type cast' : from data pointer 'void *' to function pointer 'double (__cdecl *)(double ,double )'

    aber es funktionert jetzt jedenfalls. 👍
    was für konvertierungsprobleme meinst du ? mit was für problemen muss ich rechnen ?
    ausser das ich irgendwo speichern muss, welcher voidzeiger welchen funktionstypen hat sehe ich keine schwierigkeiten.



  • pointer experienced schrieb:

    mit was für problemen muss ich rechnen ?
    ausser das ich irgendwo speichern muss, welcher voidzeiger welchen funktionstypen hat sehe ich keine schwierigkeiten.

    Du hast es hiermit selbst beantwortet - der Compiler gibt ja seine warning nicht zum Spaß aus. ⚠
    Mit dem void* cast entziehst Du ihm die Überwachung der Typenrichtigkeit zur Compiletime, weshalb Du zur Runtime sicherstellen mußt, daß der Pointer richtig aufgelöst wird. Du wirst selber merken, daß sich bei solchen Konstrukten eingeschlichene Fehler nur schlecht finden lassen und Pointer, die "irgendwas" sind und nur an bestimmten Stellen eine bestimmte Bedeutung bekommen, als "leseunfreundlich" gelten.

    Sofern anders lösbar, solltest Du insbesondere das Pointercasting lassen - ist zwar jetzt nicht unbedingt meine Meinung, aber so ähnlich äußern sich die C-Puristen. ⚠



  • pointercrash() schrieb:

    Sofern anders lösbar, solltest Du insbesondere das Pointercasting lassen - ist zwar jetzt nicht unbedingt meine Meinung, aber so ähnlich äußern sich die C-Puristen. ⚠

    ja, es ist anders lösbar, ich könnte eine union innerhalb einer struktur benutzen und in der struktur den funktionstyp speichern.

    allerdings kann ich die union nicht so bequem wie eine struktur initialisieren, sondern brauche eine initialisierungs-funktion.
    dann ist aber wenigstens das meckern des compilers weg.
    so sollte ich es vielleicht machen. 🙂


Anmelden zum Antworten