Вызвать функцию, названную в строковой переменной в C

Я хочу вызвать функцию, используя переменную. Возможно ли это в C?

Фактически, я хочу сделать, получить имя функции от пользователя и сохранить его в переменной. Теперь я хочу вызвать функцию, имя которой сохранено. Может ли кто-нибудь сказать мне, как это можно сделать в C?

Я хочу разработать игровой движок AI для игры с двумя игроками. Две программы без основной функции, которые реализуют логику выигрыша в игре, будут переданы в игровой движок. Позвольте мне пояснить, что имена программ будут такими же, как и в простых функциях в программе, реализующих логику выигрыша игры.

Поэтому, когда пользователь вводит имя первого и второго игроков, я могу хранить их в двух разных переменных. Теперь, поскольку имена премьер-функций такие же, как имена программ, я намереваюсь вызывать функции с переменными, содержащими имена прог.

C не поддерживает такую ​​операцию (языки, на которых есть reflection). Лучшее, что вы сможете сделать, это создать таблицу поиска из имен функций в указатели на функции и использовать ее для определения, какую функцию вызывать. Или вы можете использовать оператор switch.

Лучшее, что вы можете сделать, это что-то вроде этого:

 #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); } 

Это использует числа вместо строк, чтобы идентифицировать функции, но делать это со строками – это просто преобразование строки в нужную функцию.

Что вы делаете, точно? Если только ради любопытства, здесь вы идете, но если вы пытаетесь решить проблему с этим, я уверен, что есть способ, который лучше подходит для вашей задачи.

редактировать

В связи с вашими изменениями вы, безусловно, захотите пойти с ответом onebyone .

Вы хотите, чтобы ваш пользователь создал динамические библиотеки (то есть общий объект [.so] в Linux и динамическую библиотеку ссылок [.dll] в Windows).

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

Хотя это не совсем практическое решение, я уверен, вы могли бы назвать функцию строкой, прочитав программу в своем собственном исполняемом файле и проанализируя таблицу символов. Таблица символов должна содержать имя функции, а также первый адрес инструкции. Затем вы можете разместить этот адрес в переменной указателя функции и вызвать его.

Думаю, я могу попытаться взломать это.

EDIT: Пожалуйста, никто никогда не писал настоящий код, как это, но вот как вы можете вызвать функцию, используя строку для Linux ELF двоичного кода с неповрежденной таблицей символов (требуется 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; } 

Это невозможно в чистом C, однако вы можете играть в трюки с dll. Поместите все функции, которые вы хотите выбрать, в dll, затем используйте dlsym (или GetProcAddress в Windows или любой другой API, который предлагает ваша система), чтобы получить указатель на функцию по имени и вызвать с помощью этого.

Это не работает на некоторых платформах либо потому, что у них вообще нет библиотек, либо потому, что, как и Symbian, функции в dll не могут быть доступны по имени во время выполнения, только по числу.

Имейте в виду, что если ваш пользователь обманывает вас в выборе функции, которая не имеет правильных параметров для вызова, который вы хотите сделать, ваша программа пойдет не так. C действительно не предназначен для того, чтобы справляться с такими вещами.

 #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"); } 

Как утверждают другие, верно, что C не имеет механизма отражения. Но вы можете добиться такого поведения, используя динамически загружаемую библиотеку / общий объект. Фактически вы можете загрузить динамическую библиотеку, а затем вы можете вызвать функции в dll / so с их именем! Это не C и ОС, но это так. Он использует dlopen для Linux и LoadLibrary для Windows. Вы можете найти библиотеки, которые делают работу для вас, как gtk / glib .

Это то, что вы пытаетесь:

 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; } 

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

Языки, такие как C # и Java, поддерживают reflection, облегчая выполнение позднего связывания.

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

Во-первых, вы найдете множество мест в Интернете (включая Википедию), которые расскажут вам, что способ открыть основную программу в качестве библиотеки – это вызвать dlopen (3), как этот dlopen(NULL, 0) . Это не будет работать для glibc, потому что должен быть указан флаг привязки, поскольку на странице руководства четко указано:

В флаг должно быть включено одно из следующих двух значений:
RTLD_LAZY
Выполняйте ленивую привязку. Разрешать символы только в качестве кода …
RTLD_NOW
Если это значение указано или переменная среды …

Я не думаю, что вы выбираете здесь, потому что вы собираетесь связать все символы из статической библиотеки в свой исполняемый файл.

Это приводит нас к следующей проблеме. Компилятор не будет включать символы из вашей статической библиотеки в ваш исполняемый файл, потому что на них не ссылаются . Способ заставить GNU-компоновщик включать символы в любом случае: -Wl,--whole-archive path/to/static/lib.a -Wl,--no-whole-archive как описывает этот ответ. Способ заставить компоновщик Mac OS X включать все символы из вашей статической библиотеки: -Wl,-force_load path/to/static/lib.a

C массивы могут быть проиндексированы только с интегральными типами. Поэтому напишите строки отображения хеш-таблицы для указателей функций.

Также возможно посмотреть на объект на языках Python, Lua и других языках сценариев, чтобы внедрить их время выполнения в программу на C: части кода C затем могут обрабатываться с помощью языка сценариев.

Alt’ly, некоторые люди кодируют расширения языка скрипта в C. Затем они могут иметь доступ к скорости и низкому уровню C в своих сценариях.

Вы рано или поздно обнаружите, что использование динамически типизированных идиом языка скриптов – например, eval (), и размытая линия между именем функции и функции, а также код, который зависит от массивов-помощников – на C, возможен, но в конечном итоге болезненный.

Представляем функцию Nginx-c. Это модуль NGINX, который позволяет связать ваше приложение .so (c / c ++) в контексте сервера и вызвать функцию приложения .so в директиве location. Вы можете реализовать кеш данных данных nginx share через функцию nginx c ( https://github.com/Taymindis/nginx-c-function/wiki/Nginx-Cache-Data-via-nginx-c-функция ). Это для разработчиков, которые любят размещать сервер c. https://github.com/Taymindis/nginx-c-function

введите описание изображения здесь

  • Как изменить реализацию (обход) функции, объявленной извне
  • Отправить форму при нажатии Enter с помощью AngularJS
  • Операторы перегрузки как функция-член или функция не-член (друга)?
  • Вызов библиотеки C ++ в C #
  • Вложенная функция в C
  • Выполнить функцию jQuery после выполнения другой функции
  • В MATLAB, могу ли я иметь скрипт и определение функции в том же файле?
  • Два способа определения функций в Scala. В чем разница?
  • Использование нескольких .cpp-файлов в c ++-программе?
  • Заполните непересекающиеся пустые ячейки со значением из ячейки над первым пробелом
  • Вложенные функции в Java
  • Давайте будем гением компьютера.