Dynamische Funktionszeiger



  • Hallo,

    gibt es irgend eine Möglichkeit, Funktionen direkt über einen char* zur Laufzeit aufzurufen (wie bei PHP) oder muss man es tatsächlich über if/else if ... bzw. switch statements machen?

    Ich habe viel gegoogelt und fürchte, dass die Antwort nein ist. 😕

    Danke!

    Peilnix



  • wie in php gehts nicht. Du müsstes dir if/else bauen oder eine hashmap anlegen die char* mit pointer verknüpft. Direkt aus dem char* den funktionsnamen auslesen und diese aufrufen, geht nicht.



  • Wieso denn nicht?
    Also Es gibt doch funktionszeiger, und der typ des Funktionszeigers hängt von den typen der Parameter und vom Rückgabwert ab.

    Nehmen wir an, du hast eine fkt, die einen double wert als parameter erhält und einen zurückgibt:

    double func_x(double);
    

    dann kannst du einen Funktionszeiger deklarieren und den dann mit der funktion initialisieren:

    double (*func_ptr) (double) = NULL;
    func_ptr = func_x;
    

    Dann kannst du mit dem Zeiger alles aufrufen als obs ne normale funktion wär:

    y = func_ptr(x);
    

    Alles nachzulesen auf: http://www.newty.de/fpt/fpt.html

    Übrigens der erste Link von Google, wenn man "Function Pointer" eingibt.

    [edit] Ach ja, du wolltest doch nen char* haben oder? Naja, seien wir froh dass C nicht Typsicher ist...

    char* char_ptr;
    void* void_ptr;
    
    void_ptr = func_ptr;
    char_ptr = (char*) void_ptr;
    

    [/edit]



  • @maxi

    Die Idee mit der HashTable ist ziemlich gut und weniger "nervig" als ein nicht endendes if bzw. switch "Geschalte" ... aber hoffentlich hat drPhilGuth recht.

    @drPhilGuth

    Das ist auch ein interessanter Anstoß. Leider funktioniert das cdecl Attribut bei mir nicht (der compiler ignoriert es, vielleicht, weil 64Bit), aber ich forsche gerade weiter:

    http://www.unixwiz.net/techtips/gnu-c-attributes.html
    http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html

    ... und hoffe, dass c tatsächlich nicht typesafe ist!

    Ich danke Euch!

    Peilnix



  • peilnix schrieb:

    ... und hoffe, dass c tatsächlich nicht typesafe ist!

    keine sorge,
    C ist ziemlich 'low levelig' und betrachtet jedes 'objekt' nur als einen haufen bytes im speicher. 😉



  • Hallo Leute,

    ich hab mich echt krummgelegt, aber ich habe es nicht geschafft!

    @DrPhilGuth

    In dem FunctionPointer Tutorial wird im Endeffekt eben doch "geswitched". Was ich gerne machen würde ist:

    -> String aus DB einlesen.
    -> String als Funktionspointer übergeben.

    Und das scheint eben doch nicht zu gehen.

    #include <stdlib.h>
    #include <stdio.h>
    
    void (*zeiger)();
    
    void keks()  __attribute__((decl));     // GNU GCC
    void keks() {puts("\nEs geht ja doch!\n");}
    int main() {
    	zeiger="keks";
    	zeiger();
    }
    

    Terminiert eben doch. Vielleicht habe ich aber die Logik einfach nicht verstanden 😕

    @Undertaker

    Das mag ich ja gerade, aber es scheint nicht möglich zu sein, zur Laufzeit den Zeiger auf die Funktion zu definieren (siehe Beitrag von Maxi).

    Euer Peilnix



  • peilnix schrieb:

    ich hab mich echt krummgelegt, aber ich habe es nicht geschafft!

    @DrPhilGuth

    In dem FunctionPointer Tutorial wird im Endeffekt eben doch "geswitched". Was ich gerne machen würde ist:

    -> String aus DB einlesen.
    -> String als Funktionspointer übergeben.

    Und das scheint eben doch nicht zu gehen.

    #include <stdlib.h>
    #include <stdio.h>
    
    void (*zeiger)();
    
    void keks()  __attribute__((decl));     // GNU GCC
    void keks() {puts("\nEs geht ja doch!\n");}
    int main() {
    	zeiger="keks";
    	zeiger();
    }
    

    Terminiert eben doch. Vielleicht habe ich aber die Logik einfach nicht verstanden 😕

    Da gibt es nicht viel zu verstehen: Es geht nicht. DrPhils Antwort ging ziemlich am Thema vorbei (nix für ungut).



  • peilnix schrieb:

    Und das scheint eben doch nicht zu gehen.

    probier mal dies:

    #include <stdio.h>
    
    void f1 (void)
    {
       puts ("ich bin f1");
    }
    
    void f2 (void)
    {
       puts ("...und ich nicht!");
    }
    
    void main (void)
    {
       void (*ptr)(void);
    
       ptr = f1;
       ptr();
    
       ptr = f2;
       ptr();
    }
    

    🙂



  • Ok, d.h., wenn es so nicht geht, dann könnte ich ein "Vorprogramm" schreiben, dass die Funktionsnamen aus der Datenbank einliest, eine Datei erzeugt mit den entsprechenden "switch" statements, dann gcc per system aufruft.

    Anschließend kann ich das Hauptprogramm starten, das diese "Funktionsfactory" dann includiert ...

    ... scheint die einfachste Lösung zu sein?!

    Allen ein großes Dankeschön,

    Der Peilnix



  • @Undertaker

    So kann ich "on the run" die Adresse des Zeigers verändern. Funktioniert. Aber die Adresse des Zeigers aus einem char* heraus speisen?! Hast Du da auch eine Idee?

    Wäre soooo bequem im Vergleich zu dem Vorschlag, den ich eben gepostet habe 🙂

    ... und noch einmal Danke!

    Peilnix



  • peilnix schrieb:

    So kann ich "on the run" die Adresse des Zeigers verändern. Funktioniert. Aber die Adresse des Zeigers aus einem char* heraus speisen?! Hast Du da auch eine Idee?

    das geht leider nicht. funktionsnamen sind ja nur im quelltext da. im binary gibt's die dann nicht mehr. du könntest ein array anlegen mit function pointern und ein zweites mit den funktionsnamen o.ä. (oder ein struct-array).
    beispiel:

    #include <stdio.h>
    #include <string.h>
    
    void f1 (void)
    {
       puts ("ich bin f1");
    }
    
    void f2 (void)
    {
       puts ("...und ich nicht!");
    }
    
    struct meta
    {
       void (*ptr)(void);
       char *name;
    } m[] = {{f1, "f1"}, {f2, "f2"}};
    
    void exec (char *n)
    {
       int s;
       for (s=0; s<sizeof(m)/sizeof(struct meta); s++)
       {
          if (strcmp (m[s].name, n) == 0)
          {
             m[s].ptr();
             break;
          }
       }         
    }
    
    void main (void)
    {
       exec ("f1");
       exec ("f2");
    }
    

    ungetestet.
    🙂



  • Lieber Undertaker,

    das scheint mir eine sehr elegante Lösung zu sein!
    ... und dass ich anders als in PHP keine (wirklich) dynamischen Funktionszeiger benutzen kann, damit fange ich langsam an, mich abzufinden!

    Peilnix



  • peilnix schrieb:

    ... und dass ich anders als in PHP keine (wirklich) dynamischen Funktionszeiger benutzen kann, damit fange ich langsam an, mich abzufinden!

    PHP hat ja auch keine function pointer (oder doch?), aber man kann dort funktionen anhand des namens herumreichen, zur laufzeit dynamischen code erzeugen und ausführen usw.
    jaja, so'ne flexibilität ist ein grosser vorteil von interpretersprachen u.ä., das schimpft sich, glaub' ich 'reflective programming.'
    🙂



  • @Undertaker

    Das Stichwort Assemblercode hat mir erklärt, warum es in PHP funktioniert: Dort wird geparst, ohne dass in Assemblecode übersetzt wird, weshalb man mit

    func (blub, blah) {}

    $a="func";
    a (blub, blah)

    eine Funktion aufrufen kann. Das hat mir damals (als ich ein PHP_Projekte hatte) sehr viel Mühe erspart; aber jetzt habe ich ja ein quasi-workaround für C 🙂



  • peilnix schrieb:

    eine Funktion aufrufen kann. Das hat mir damals (als ich ein PHP_Projekte hatte) sehr viel Mühe erspart; aber jetzt habe ich ja ein quasi-workaround für C 🙂

    Nimmst du es mir übel wenn ich den Workaround als Designfehler bezeichne?

    Andere Frage: Warum willst du so eine bestimmte Funktion ausführen? Wie du schon bemerkt hast ist C nicht PHP. Dementsprechend sind die Lösungswege unterschiedlich. PHP-artiges Verhalten in C nachzubauen macht da nicht wirklich viel Sinn.



  • Undertaker schrieb:

    jaja, so'ne flexibilität ist ein grosser vorteil von interpretersprachen u.ä., das schimpft sich, glaub' ich 'reflective programming.'

    Hat aber nicht direkt was mit Interpretersprachen zu tun.



  • Bashar schrieb:

    Undertaker schrieb:

    jaja, so'ne flexibilität ist ein grosser vorteil von interpretersprachen u.ä., das schimpft sich, glaub' ich 'reflective programming.'

    Hat aber nicht direkt was mit Interpretersprachen zu tun.

    ja, sorry, du hast natürlich recht. es ist keine frage von 'interpretersprachen', sondern die laufzeitumgebung oder 'virtual machine' muss sowas anbieten.
    🙂



  • hmm. naja. nachdem ich den thread nochmal durchlas, kam mir die frage auf: wieso meinen einige hier, dass es mit c nicht möglich sei, funktionen bei ihrem namen aufzurufen?

    es geht, ist aber nicht portable und setzt voraus, dass eine vollständige symboltabelle für die internen funktionen existiert. für die so/dll-funktionen hat man eh immer eine.
    hier ein beispiel für unixoide systeme (sollte auf allen so laufen):

    #include <stdlib.h>
    #include <stdio.h>
    #include <dlfcn.h>
    
    int test()
    {
    	printf("test() wurde aufgerufen.\n");
    	return 0;
    }
    
    int main()
    {
    	void* handle;
    	handle = dlopen(0,RTLD_LAZY); //öffnen der globale symbol tabelle
    	if (!handle)
    	{
                    //das kann eigentlich nicht passieren.
    		printf("Unable to open global symbol table.\n"); 
    		exit(0);
    	}
    	((int(*)())dlsym(handle,"test"))(); //hier der aufruf der funktion "test" mittels ihres namens.
    //EDIT ich sollte noch darauf hinweisen, dass man natürlich auch alle per
    //so eingebunden funktionen aufrufen kann:
    	((int(*)())(((void*(*)(void*,const char*))dlsym(handle,"dlsym"))(handle,"test")))();
    	dlclose(handle); //saubere schweinchen räumen hinterher wieder auf
    	return 0;
    }
    

    kompilierbar bspw so: gcc main.c -ldl -rdynamic
    das -rdynamic ist wichtig, sonst kann es mit internen funktionen oder anderen ungenutzten funktionen schief gehen.

    ist alles in allem schönes sauberes c mit ein bisschen posix-spaß.

    ich nehme mal an, dass es unter windows etwas äquivalentes zu dlopen und co gibt. da kenne ich mich aber nicht so aus.

    achja: wenn irgendjemand auf die hirnverbrannte idee kommt, dass tatsächlich als alternative zu nutzen, werde ich ihm im traum erscheinen, bis er es wieder lässt. 😉



  • Ja, unter Windows würde sowas auch gehen:
    Du schreibst dir alle benötigten Funktionen in eine extra DLL und machst dann ungefähr sowas:

    ...
    std::string fn_name;
    read_fn_name_from_db(fn_name);
    
    HMODULE lib_handle = LoadLibrary("myfunctions.dll");
    my_fn = (void (*)(void))GetProcAddress(lib_handle, fn_name.c_str());
    
    (my_fn)();
    ...
    

    Aber wie ghorst schon schreibt: Das ist böse 😃

    Grüße,

    Martin



  • sicher das es keine möglichkeit gibt auf die symboltabelle des programms zuzugreifen? würde mich jetzt wundern. (oder habe ich dich nur durch die nutzung von dlopen verwirrt? unter unix ist mit dem paramter 0 anstelle des namens einer lib gemeint, dass man die symboltabelle des aktuellen programms inklusive aller gelandenen libs haben will.)


Anmelden zum Antworten