Chiama una funzione denominata in una variabile stringa in C

Voglio chiamare una funzione usando una variabile. È ansible in C?

In realtà, quello che voglio fare è ottenere il nome della funzione da parte dell’utente e memorizzarlo in una variabile. Ora voglio chiamare la funzione che ha il suo nome memorizzato. Qualcuno può dirmi come si può fare in C?

Voglio sviluppare un motore di gioco AI per un gioco a due giocatori. Due programmi senza nessuna funzione principale che implementano la logica per vincere il gioco verranno inseriti nel motore di gioco. Vorrei essere chiaro che i nomi dei programmi saranno uguali a quelli delle primefunctions del programma che implementano la logica per vincere il gioco.

Quindi, quando l’utente inserisce il nome del primo e del secondo giocatore, posso memorizzarli in 2 variabili diverse. Ora, dato che i nomi delle primehe sono uguali a quelli dei nomi dei programmi, intendo chiamare le funzioni con le variabili che contengono i nomi prog.

C non supporta questo tipo di operazione (le lingue che hanno una riflessione). Il meglio che si sarà in grado di fare è creare una tabella di ricerca dai nomi di funzioni ai puntatori di funzione e usarla per capire quale funzione chiamare. O potresti usare un’istruzione switch.

Il meglio che puoi fare è qualcosa del genere:

 #include  // functions void foo(int i); void bar(int i); // function type typedef void (*FunctionCallback)(int); FunctionCallback functions[] = {&foo, &bar}; int main(void) { // get function id int i = 0; scanf("%i", &i); // check id if( i >= sizeof(functions)) { printf("Invalid function id: %i", i); return 1; } // call function functions[i](i); return 0; } void foo(int i) { printf("In foo() with: %i", i); } void bar(int i) { printf("In bar() with: %i", i); } 

Questo utilizza numeri invece di stringhe per identificare le funzioni, ma farlo con le stringhe è semplicemente una questione di conversione della stringa nella funzione corretta.

Che cosa stai facendo, esattamente? Se solo per curiosità, ecco, ma se stai cercando di risolvere un problema con questo, sono sicuro che c’è un modo più adatto al tuo compito.

modificare

Preoccupato per la tua modifica, vorrai andare con la risposta di oneby , di sicuro.

Volete che il vostro utente costruisca librerie dinamiche (questo è un object condiviso [.so] in Linux e una libreria di collegamenti dinamici [.dll] in Windows).

Una volta fatto ciò, se ti forniscono il nome della loro libreria, puoi chiedere al sistema operativo di caricare quella libreria e richiedere un puntatore a una funzione all’interno di quella libreria.

Anche se questa non è esattamente una soluzione pratica, scommetto che si potrebbe certamente chiamare una funzione con una stringa avendo un programma letto nel proprio eseguibile e analizzando la tabella dei simboli. La tabella dei simboli dovrebbe contenere il nome della funzione e il suo primo indirizzo di istruzioni. È quindi ansible inserire questo indirizzo in una variabile del puntatore di funzione e chiamarlo.

Penso che potrei provare a sferrarlo.

EDIT: Si prega di non scrivere mai codice reale come questo, ma ecco come si può chiamare una funzione usando una stringa per un binario ELF Linux con una tabella di simboli intatta (richiede libelf):

 #include  #include  #include  #include  #include  #include  void callMe() { printf("callMe called\n"); } int main(int argc, char **argv) { Elf64_Shdr * shdr; Elf64_Ehdr * ehdr; Elf * elf; Elf_Scn * scn; Elf_Data * data; int cnt; void (*fp)() = NULL; int fd = 0; /* This is probably Linux specific - Read in our own executable*/ if ((fd = open("/proc/self/exe", O_RDONLY)) == -1) exit(1); elf_version(EV_CURRENT); if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { fprintf(stderr, "file is not an ELF binary\n"); exit(1); } /* Let's get the elf sections */ if (((ehdr = elf64_getehdr(elf)) == NULL) || ((scn = elf_getscn(elf, ehdr->e_shstrndx)) == NULL) || ((data = elf_getdata(scn, NULL)) == NULL)) { fprintf(stderr, "Failed to get SOMETHING\n"); exit(1); } /* Let's go through each elf section looking for the symbol table */ for (cnt = 1, scn = NULL; scn = elf_nextscn(elf, scn); cnt++) { if ((shdr = elf64_getshdr(scn)) == NULL) exit(1); if (shdr->sh_type == SHT_SYMTAB) { char *name; char *strName; data = 0; if ((data = elf_getdata(scn, data)) == 0 || data->d_size == 0) { fprintf(stderr, "No data in symbol table\n"); exit(1); } Elf64_Sym *esym = (Elf64_Sym*) data->d_buf; Elf64_Sym *lastsym = (Elf64_Sym*) ((char*) data->d_buf + data->d_size); /* Look through all symbols */ for (; esym < lastsym; esym++) { if ((esym->st_value == 0) || (ELF64_ST_BIND(esym->st_info)== STB_WEAK) || (ELF64_ST_BIND(esym->st_info)== STB_NUM) || (ELF64_ST_TYPE(esym->st_info)!= STT_FUNC)) continue; name = elf_strptr(elf,shdr->sh_link , (size_t)esym->st_name); if(!name){ fprintf(stderr,"%sn",elf_errmsg(elf_errno())); exit(-1); } /* This could obviously be a generic string */ if(strcmp("callMe", name) == 0 ) { printf("Found callMe @ %x\n", esym->st_value); fp = esym->st_value; } } /* Call and hope we don't segfault!*/ fp(); elf_end(elf); return 0; } 

Non è ansible in C pura, tuttavia potresti essere in grado di giocare brutti scherzi. Metti tutte le funzioni che vuoi selezionare in una DLL, quindi usa dlsym (o GetProcAddress su Windows o qualsiasi altra API offerta dal tuo sistema) per ottenere il puntatore della funzione per nome e chiama usando quello.

Questo non funziona su alcune piattaforms, sia perché non hanno le DLL, né perché come in Symbian le funzioni nella DLL non sono accessibili per nome in fase di runtime, solo per numero.

Tieni presente che se il tuo utente cerca di selezionare una funzione che non ha i parametri corretti per la chiamata che vuoi fare, allora il tuo programma andrà storto. C davvero non è progettato per far fronte a questo genere di cose.

 #include  #include  void function_a(void) { printf("Function A\n"); } void function_b(void) { printf("Function B\n"); } void function_c(void) { printf("Function C\n"); } void function_d(void) { printf("Function D\n"); } void function_e(void) { printf("Function E\n"); } const static struct { const char *name; void (*func)(void); } function_map [] = { { "function_a", function_a }, { "function_b", function_b }, { "function_c", function_c }, { "function_d", function_d }, { "function_e", function_e }, }; int call_function(const char *name) { int i; for (i = 0; i < (sizeof(function_map) / sizeof(function_map[0])); i++) { if (!strcmp(function_map[i].name, name) && function_map[i].func) { function_map[i].func(); return 0; } } return -1; } int main() { call_function("function_a"); call_function("function_c"); call_function("function_e"); } 

Come detto da altri, è vero che C non ha meccanismi di riflessione. Ma puoi ottenere questo tipo di comportamento utilizzando la libreria / l’object condiviso caricato dynamicmente. In effetti puoi caricare una libreria dynamic e quindi puoi chiamare le funzioni nella dll / so con il loro nome! Non è specifico per C e OS, ma è così. Usa dlopen su Linux e LoadLibrary su Windows. Puoi trovare librerie che fanno il lavoro per te come gtk / glib .

È questo quello che stai provando:

 void foo() { printf("foo called\n"); } void bar() { printf("bar called\n"); } int main() { char fun[10] = {'\0'}; printf("Enter function name (max 9 characters):"); scanf("%s",fun); if(strcmpi(fun, "foo") == 0) { foo(); } else if(strcmpi(fun, "bar") == 0) { bar(); } else { printf("Function not found\n"); } return 0; } 

Se capisco correttamente la tua domanda, vuoi usare l’associazione tardiva per chiamare una funzione C. Questo non è qualcosa che normalmente si può fare in C. I nomi simbolici (come i nomi delle funzioni) non sono memorizzati nel codice generato dal compilatore C. Probabilmente potresti lasciare che il compilatore emetta dei simboli, e poi usarli per eseguire l’associazione tardiva, ma la tecnica varierebbe dal compilatore al compilatore e probabilmente non varrebbe la pena.

Linguaggi come C # e Java supportano la riflessione rendendo facile l’esecuzione di un binding tardivo.

Ho appena provato l’ approccio di Steve Jessop usando una libreria collegata staticamente come suggerito da Williham Totland nei commenti e si è rivelato non banale.

In primo luogo, troverai un sacco di posti sul Web (inclusa Wikipedia) che ti diranno che il modo per aprire il tuo programma principale come libreria è chiamare dlopen (3) come questo dlopen(NULL, 0) . Questo non funzionerà con glibc perché deve essere specificato un flag di binding, come afferma chiaramente la pagina man:

Uno dei seguenti due valori deve essere incluso nel flag:
RTLD_LAZY
Esegui il binding pigro. Risolvi i simboli solo come codice …
RTLD_NOW
Se questo valore è specificato, o la variabile di ambiente …

Non penso che quello che scegli qui sia importante perché dovrai colbind tutti i simboli della libreria statica al tuo eseguibile.

Questo ci porta al prossimo problema. Il linker non includerà i simboli della tua libreria statica nel tuo eseguibile perché non sono referenziati . Il modo per forzare il linker GNU a includere i simboli è comunque -Wl,--whole-archive path/to/static/lib.a -Wl,--no-whole-archive come descritto da tale risposta. Il modo per forzare il linker di Mac OS X a includere tutti i simboli dalla libreria statica è -Wl,-force_load path/to/static/lib.a

I C array possono essere indicizzati solo con tipi interi. Quindi scrivere una stringa hash per mappare le stringhe ai puntatori di funzione.

Potrebbe anche valere la pena esaminare la struttura in Python, Lua, altri linguaggi di script per incorporare il loro tempo di esecuzione in un programma C: parti del codice C possono quindi essere manipolate con il linguaggio di scripting.

Altamente, alcune persone codificano le estensioni del linguaggio di script in C. Quindi possono avere la velocità e l’accesso a basso livello di C nei loro script.

Scoprirai, prima o poi, che l’uso di idiomi di linguaggio di script digitati dynamicmente – come eval () e la linea sfocata tra funzione e nome di funzione e codice che dipende dagli array assoc – in C è ansible ma in definitiva doloroso.

Presentazione della funzione Nginx-c. Si tratta di un modulo NGINX che consente di colbind l’applicazione .so (c / c ++) nel contesto del server e chiamare la funzione dell’applicazione .so nella direttiva location. È ansible implementare la cache dei dati della memoria di condivisione nginx tramite la funzione nginx c ( https://github.com/Taymindis/nginx-c-function/wiki/Nginx-Cache-Data-via-nginx-c-function ). Questo è per gli sviluppatori che amano ospitare il server c. https://github.com/Taymindis/nginx-c-function

inserisci la descrizione dell'immagine qui