2010-02-02 9 views
6

¡Vi esta pregunta en uno de los rompecabezas de C! ¿Es esto realmente posible?¿Es posible llamar a una función C, dado su nombre como una cadena?

¿Cómo puedo llamar a una función, dado su nombre como una cadena? ¿es posible usar una cadena que se lee con scanf para ser utilizada directamente para llamar a una función?

ya he pensado en si (strcmp (str, "cadena")) a continuación, llame a la función.

pero ¿hay algún otro enfoque?

+0

sí Qt utiliza para implementar signalslot – yesraaj

+1

El fundamento de la cuestión es el hecho de que los compiladores no ponen los nombres de las funciones en ejecutables (excepto tal vez para las versiones de depuración). No hay ningún requisito en el lenguaje C para que un compilador conserve los nombres de las funciones. –

+1

¿Qué es una función? Imposible decir después de que el código ha sido compilado y vinculado. El compilador puede hacer lo que quiera: crear nuevas funciones, eliminar funciones antiguas (alineación), etc. –

Respuesta

3

Usted podría ser astuto:

if (strcmp (fn, "function1") == 0) function1(); 
if (strcmp (fn, "function2") == 0) function2(); 
if (strcmp (fn, "function3") == 0) function3(); 

o puede utilizar dlopen y dlsym (o equivalente) en función de su entorno de ejecución, pero estos no son C estándar, por lo que podría no ser una opción.

Eso es todo lo que tengo aparte de increíblemente portátil con el archivo ejecutable o el diseño de macros horribles (que probablemente se reduciría a la colección de if declaraciones mostradas anteriormente, o una matriz de punteros a función de algún tipo).

11

Dado que ninguna mención de lo que es una función, ni sus parámetros, me imagino que algo como esto:

 
typedef void (*foo)(); 
struct puzzleFoo{ 
    char *function_name; 
    foo *fn; 
}; 

Crear una tabla de búsqueda basada en la estructura, utilizando un parámetro de cadena

 
struct puzzleFoo *Lookup(const char *function_name); 

Luego itere a través de una matriz/lista buscando puzzleFoo 's function_nam ey ejecute el puntero de función llamado fn.

+2

Ese 'char *' en 'puzzleFoo' debe ser' const char * '. Además, puedes definir una macro para crear un 'puzzleFoo' para ti:' #define FN_ENTRY (funcPtr) {#funcPtr, y funcPtr} 'Entonces solo dices' puzzleFoo functionList [] = {FN_ENTRY (fooA), FN_ENTRY (fooB) }; size_t functionListLength = sizeof (functionList)/sizeof (puzzleFoo); 'por ejemplo. –

+0

@Mike D: ¡Es una excelente sugerencia! :) ¡Gracias por tu contribución! :) – t0mm13b

2

Depende de la función a la que desee llamar.
Si se trata de una función de DLL u otro tipo de binario exportado, por supuesto que puede hacerlo.
Hay una API normal para esto.
Si esta función no se exporta y se compiló en ejecutable, por supuesto que no, ya que la información como nombres de func se truncan.

3

Si la función está disponible en una biblioteca compartida, es posible que pueda cargar la biblioteca compartida en tiempo de ejecución y obtener acceso a la tabla de símbolos, que sería convertible en nombres de función y punteros. En cuanto a asegurarse de que la función que llama firma es correcta, usted está solo.

VxWorks hace esto por su carcasa incorporada.

1

Escribí esto en la cumbre de mi cabeza, no hay garantía de que compile, pero es probable que esté muy cerca. Lo suficientemente bueno para una entrevista.

#include <stdio.h> 
#include <unistd.h> 
char *program = "#include<stdio.h>\ 
\ 
void Func1() { printf("hi\n"); }\ 
void Func2() { printf("hello\n"); }\ 
void Func3() { printf("hey\n"); }\ 
main() {\ 
"; 
char *program1 = "}\n"; 

static in ContainsFunction(char *func) 
{ 
    char *match = strstr(func, program); 
    if (!match || match - program < 5) 
     return 0; 
    int len = strlen(func); 
    return strcmp(match-5, program) == 0 && match[len + 1] == '(' && isalnum(func[len-1]); 
} 

static void Compile(char *prog) 
{ 
    pid_t pid = fork(); 
    if (pid == 0) { 
     execl("/usr/bin/gcc", "gcc", prog, (char *)0); 
    } 
    else if (pid < 0) { printf("fork fail\n"); } 
    else { 
     pid_t ws = wait(); 
    } 
} 

static void Execute(char *prog) 
{ 
    pid_t pid = fork(); 
    if (pid == 0) { 
     execl(prog, prog, (char *)0); 
    } 
    else if (pid < 0) { printf("fork fail\n"); } 
    else { 
     pid_t ws = wait(); 
    } 
} 

static void CallFunction(char *funcname) 
{ 
    FILE *fp = fopen("foo.c", "w"); 
    fputs(program, fp); 
    fprintf(fp, "\t%s();\n", funcname); 
    fputs(fp, program1); 
    fclose(fp); 

    Compile("foo.c"); 
    Execute("a.out"); 
} 

int main() 
{ 
    char funcname[8192]; /* too small, fail */ 
    puts("Who ya gonna call?"); 
    gets(funcname); 
    if (ContainsFunction(funcname)) { CallFunction(funcname)); } 
    else { printf("fail - no function %s\n", funcname); } 
}  
3

Sí, a veces.

En Linux, se puede usar dlopen() que abra una biblioteca compartida que contiene la función que desee o incluso acceder a la corriente ejecutable y localizar su función mediante el dlsym()

En Windows, lo más habitual es llamar LoadLibrary() y GetProcAddress() respectivamente.

Si las tablas de símbolos para las bibliotecas en cuestión se han eliminado o, en algunos casos, si los métodos son estáticos/privados, no podrá acceder a ellos utilizando este enfoque.

Además, no olvide, si su biblioteca está escrita en C++, entonces puede que tenga que contender con el cambio de nombre. Necesitarás entender el método de manipulación utilizado por el compilador antes.

10

Con POSIX.1-2001 debe usar dlopen() and dlsym(). En Windows use GetModuleHandleEx() y GetProcAddress().

He aquí un ejemplo textualmente del manual que carga la función denominada "cos" de la biblioteca matemática y determina el coseno de 2.0:

#include <stdio.h> 
#include <stdlib.h> 
#include <dlfcn.h> 

int 
main(int argc, char **argv) 
{ 
    void *handle; 
    double (*cosine)(double); 
    char *error; 

    handle = dlopen("libm.so", RTLD_LAZY); 
    if (!handle) { 
     fprintf(stderr, "%s\n", dlerror()); 
     exit(EXIT_FAILURE); 
    } 

    dlerror(); /* Clear any existing error */ 

    /* Writing: cosine = (double (*)(double)) dlsym(handle, "cos"); 
     would seem more natural, but the C99 standard leaves 
     casting from "void *" to a function pointer undefined. 
     The assignment used below is the POSIX.1-2003 (Technical 
     Corrigendum 1) workaround; see the Rationale for the 
     POSIX specification of dlsym(). */ 

    *(void **) (&cosine) = dlsym(handle, "cos"); 

    if ((error = dlerror()) != NULL) { 
     fprintf(stderr, "%s\n", error); 
     exit(EXIT_FAILURE); 
    } 

    printf("%f\n", (*cosine)(2.0)); 
    dlclose(handle); 
    exit(EXIT_SUCCESS); 
} 
+0

Ha, nuestras respuestas no son muy diferentes. Has proporcionado algunas cosas adicionales sobre el manejo de nombres, pero sé que los noods como muestras de código, que dan nombres de funciones nunca son suficientes. –

Cuestiones relacionadas