Dynamische Funktionszeiger
-
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
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
-
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.'
-
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.)
-
Bei DLLs ist das ja unumgänglich, aber es gibt IMO gar keinen technischen Grund, warum es eine Symboltabelle des Programms geben sollte.
-
es gibt sie zwangsweise beim linken und sie werden normalerweise auch nicht entfernt, da sie bspw für die einsprungsadresse genutzt werden. wie bei main oder dem heiß geliebten wndproc. auch gibt es ein paar tricks bei denen es notwendig ist, von einer dll auf prozeduren aus dem hauptprogramm zuzugreifen.
(wenn ich mich dumpf an meine windows-zeiten erinnere, hatte die programme da alle symboltabellen.)die frage ist daher eher, wie man darauf zugreift?
-
Man braucht genau einen Einsprungpunkt. Für WndProc und sonstige Callbacks reichen die aufgelösten Adressen, dazu braucht man keine Namen. Für die main natürlich auch nicht.
Tricks, bei denen man von der DLL aufs Hauptprogramm zugreift, erledigt man, indem man der DLL die Adressen der Funktionen übergibt. Wieder: Callback.
Ich hab zwar nicht viel Ahnung von der Winapi, und es mag sein, dass es aus ganz obskuren Gründen diese Symboltabellen wirklich gibt. Aber eine technische Notwendigkeit seh ich nach wie vor nicht. Unter Unix scheinen das ja die Debug-Symbole zu sein, die man auch ohne Probleme wegstrippen kann (womit dann auch dlsym fehlschlägt.)
-
du kannst die meisten wegstrippen. ein paar bleiben über. wenn du pic, was manchmal auch für executables nützlich sein kann, haben willst, bleiben alle, die exportierte und ähnliche funktionen betreffen, über, da hier die einsprungadressen für die einzelnen routinen erst bei programmstart ermittelt werden.
absolut notwendig ist die symboltabelle nicht, das ist keine frage. nur ist sie halt hinreichend oft vorhanden und wer so einen murkscode schreiben will, der wird schon die kontrolle über sein hauptprogramm haben...
dlsym wird nach dem strippen für alle aufrufe fehlschlagen (edit: das nächstemal erst probieren... das ganze funktioniert auch, nachdem man die datei gestript hat.), die keinen eintrag haben, aus dem gleichen grund ist auch das kompilieren mit -rdynamic wichtig, da sonst schon die einträge für nicht benutzte funktionen fehlen würden.
achja: wndproc ist kein callback im klassischen sinne und für main brauchst du entweder einen festen oder einen dynamischen einsprungpunkt. letzterer aus der symboltabelle.
zu den debugsymbolen: die sind was anderes. die umfassen so lustigen sachen wie variablen namen und codezeilen.