Можно ли вызвать функцию C, учитывая ее имя в виде строки? - PullRequest
6 голосов
/ 02 февраля 2010

Я видел этот вопрос в одной из загадок С! Это действительно возможно?

Как я могу вызвать функцию, учитывая ее имя в виде строки? Можно ли использовать строку, которая читается с Scanf, будет использоваться непосредственно для вызова функция?

Я уже подумал, что если (strcmp (str, "string")), то вызвать функцию.

а есть ли другой подход?

Ответы [ 7 ]

11 голосов
/ 02 февраля 2010

Поскольку не упоминается ни о функции, ни о ее параметрах, я представляю ее примерно так:

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

Создание таблицы поиска на основе структуры с использованием строкового параметра

struct puzzleFoo *Lookup(const char *function_name);

Затем выполните итерацию по массиву / списку в поисках puzzleFoo 's function_nam e и выполните указатель функции с именем fn.

.
10 голосов
/ 03 февраля 2010

В POSIX.1-2001 вы должны использовать dlopen () и dlsym () . В Windows используйте GetModuleHandleEx () и GetProcAddress () .

Вот дословный пример из руководства, которое загружает функцию с именем "cos" из математической библиотеки и определяет косинус 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);
}
3 голосов
/ 03 февраля 2010

Да, иногда.

В Linux вы можете использовать dlopen(), чтобы открыть общую библиотеку, содержащую нужную вам функцию, или даже получить доступ к текущему исполняемому файлу и найти свою функцию, используя dlsym()

В Windows вы обычно звоните LoadLibrary() и GetProcAddress() соответственно.

Если таблицы символов для рассматриваемых библиотек были удалены, или, в некоторых случаях, если методы являются статическими / закрытыми, вы не сможете получить к ним доступ, используя этот подход.

Кроме того, не забывайте, что если ваша библиотека написана на C ++, вам, возможно, придется бороться с искажением имен. Вам нужно понять метод искажения, применяемый компилятором, с которым вы сталкивались.

3 голосов
/ 02 февраля 2010

Если функция доступна в общей библиотеке, вы можете загрузить общую библиотеку во время выполнения и получить доступ к таблице символов, которая может быть преобразована в имена функций и указатели. Что касается проверки правильности подписи функции, то вы сами.

VxWorks делает это для встроенной оболочки.

3 голосов
/ 02 февраля 2010

Вы можете быть подлым:

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

или вы можете использовать dlopen и dlsym (или эквивалентный) в зависимости от среды выполнения, но это не стандартные C, так что это может быть не вариант.

Это почти все, что у меня есть, кроме невероятно непереносимого наполнения исполняемым файлом или разработки отвратительных макросов (которые, скорее всего, сводятся к коллекции if операторов, показанной выше в любом случае, или массиву указателей на функции какой-то).

2 голосов
/ 02 февраля 2010

Зависит от того, какую функцию вы хотите вызвать.
Если это функция из DLL или другого типа экспортированного двоичного файла - конечно, вы можете.
Для этого есть обычный API.
Если эта функция не экспортируется и была скомпилирована в исполняемый файл - конечно, нет, так как такая информация, как имена функций, усекается.

1 голос
/ 02 февраля 2010

Я записал это с ног на голову - нет гарантии, что он даже скомпилируется, но, вероятно, это довольно близко. Достаточно хорошо для интервью.

#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); }
}    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...