Я хочу вызвать функцию, используя переменную. Возможно ли это в C?
На самом деле, я хочу получить имя функции от пользователя и сохранить его в переменной. Теперь я хочу вызвать функцию, имя которой хранится в переменной. Может ли кто-нибудь подсказать мне, как это можно сделать в C?
Я хочу разработать игровой движок AI для игры двух игроков. Две программы без основной функции, реализующие логику победы в игре, будут подаваться на игровой движок. Уточню, что имена программ будут такими же, как и имена главных функций в программе, реализующих логику победы в игре.
Поэтому, когда пользователь вводит имена первого и второго игроков, я могу хранить их в двух разных переменных. Теперь, поскольку имена прайм-функций совпадают с именами программ, я собираюсь вызывать функции с переменными, содержащими имена программ.
C не поддерживает такой тип операций (языки, в которых есть отражение, поддерживают). Лучшее, что вы сможете сделать, это создать таблицу поиска от имен функций к указателям функций и использовать ее для определения того, какую функцию нужно вызвать. Или вы можете использовать оператор switch.
Лучшее, что вы можете сделать, это что-то вроде этого:
#include <stdio.h>
// 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's.
Вы хотите, чтобы ваши пользователи собирали динамические библиотеки (это разделяемый объект [.so] в Linux, и библиотека динамических ссылок [.dll] в Windows).
Как только вы это сделаете, если они сообщат вам имя своей библиотеки, вы можете попросить операционную систему загрузить эту библиотеку для вас и запросить указатель на функцию в этой библиотеке.
Хотя это вовсе'т совсем практичное решение, бьюсь об заклад, вы, безусловно, могли вызывать функцию по строке, имея программы читать В он's собственное исполняемый файл и проанализировать таблицу символов. Таблица символов должен содержать имя функции, а также он's первый адрес инструкции. Тогда вы могли поместить этот адрес в функцию переменную-указатель и вызвать его.
Я думаю, я могу попробовать, что это.
Редактировать: пожалуйста, никто и никогда не напишу такой код, но вот как вы можете вызвать функцию, используя строку для Линукс бинарных эльф с целой таблицей символов (требуется libelf):
#include <fcntl.h>
#include <stdio.h>
#include <elf.h>
#include <libelf.h>
#include <stdlib.h>
#include <string.h>
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;
}
Это's не возможно в чистом C, но вы можете играть трюки с DLL. Положить все функции, которые вы хотите, чтобы выбрать из в DLL-файл, а затем использовать функция dlsym
(или GetProcAddress
на Windows, или любой другой API-интерфейса системы), чтобы получить указатель на функцию по имени, и называть, используя которые.
Это не'т работать на некоторых платформах, либо потому, что они не'т иметь библиотеки на всех, или потому, что такие как Symbian функций в DLL может'т быть доступны по имени во время выполнения, только по номеру.
Помните, что если ваш пользователь уловок, которые вам в выборе функции, которая не'т иметь правильные параметры для вызова вы хотите сделать, то ваша программа будет идти не так. С самом деле это'т разработан, чтобы справиться с такого рода вещи.
#include <stdio.h>
#include <string.h>
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");
}
Это то, что вы ищете:
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 не имеет механизма отражения. Но вы можете добиться такого поведения с помощью динамически загружаемая библиотека/общая объекта. На самом деле вы можете загрузить динамическую библиотеку и затем вы можете вызывать функции в DLL/так с их именем ! Это не C и операционной системы конкретного, но, что's пути. Его используют функции выглядит в Linux и LoadLibrary на Windows. Вы можете найти библиотеки, которые делают работу для вас, как ГТК/Глеб.
Я попытался это решение : открыть исполняемый файл, а динамическую библиотеку с помощью функции выглядит().
Это'ы на ОС Linux Ubuntu, но к сожалению не на моей встроенной целевых руку. Я не'т знаю, почему, если это'ов зависит от glibc или версия libdl может.
На моей цели рукой, мысль ясна: " был./test8: не может динамически загружать исполняемый на".
В любом случае, код, который я использовал это.
Я скомпилировал с:
test8 ССЗ.с-ЛПНП -rdynamic
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int TEST_MyTestFunction( char *pArgPos , int Size , char Param )
{
printf("TEST_MyTestFunction\n");
return 0;
}
int main(int argc, char **argv)
{
int ret;
void *handle;
char *error;
int(*func)(char *,int, char);
handle = dlopen(argv[0], RTLD_LAZY);
dlerror();
func = (int(*)(char *,int, char)) dlsym(handle, "TEST_MyTestFunction");
error = dlerror();
char *p1 = "param1";
int p2 = 5;
int p3 = 'A';
ret = func(p1, p2, p3);
return ret;
}
Если я правильно понял ваш вопрос, вы хотите использовать позднее связывание для вызова функции языка Си. Это не то, что вы обычно можете сделать в C. Символьные имена (например, имена функций) не хранятся в коде, сгенерированном компилятором C. Возможно, вы могли бы позволить компилятору выдавать символы, а затем использовать их для выполнения позднего связывания, но техника будет отличаться от компилятора к компилятору и, вероятно, не будет стоить хлопот.
Такие языки, как C# и Java, поддерживают отражение, что упрощает выполнение позднего связывания.
Я просто пыталась Стив Джессоп'с подход, используя статически линкуемые библиотеки, как полагают Williham Totland в комментариях и это оказалось нетривиальной.
Во-первых, вы'll найти кучу мест в Интернете (включая Википедию), который скажет вам, что способ открыть свой главный программы как библиотека является вызов функции выглядит(3) наподобие этой функции выглядит(нуль, 0)`. Это не будет работать для glibc-за привязки флаг должен быть указан, как на man-странице четко сказано:
один из следующих двух значений должен быть включен в флаг:<БР /> RTLD_LAZY<БР /> выполнять позднее связывание. Только разрешить символы в качестве кода...<БР /> флагом rtld_now<БР /> если это значение не указано, или переменную окружения...
Я не'т думаю, что вы выберете здесь важно, потому что вы'вновь собирается связать все символы из статической библиотеки в исполняемый файл.
Это подводит нас к следующей проблеме. Компоновщик не будет включать в себя символы из статической библиотеки в исполняемый файл, потому что они'повторно не ссылается. Путь, чтобы заставить компоновщик GNU в любом случае включать символов -от WL,--все-архиве путь/к/статический/Либ.а-от WL,--нет-все-архиве, как этот ответ описывает. Путь, чтобы заставить компоновщик Мак ОС Х, чтобы включать все символы из статической библиотеки
-ЧШ,-force_load путь/к/статический/Либ.а`.
Введение в nginx-c-функции. Это модуль nginx, которые позволят вам связать свой .поэтому(с/C++) приложения в контексте сервера и вызвать функцию .поэтому применение в директиве location. Вы можете реализовать nginx и обмениваться данными кэш-памяти через функцию nginx и с(https://github.com/Taymindis/nginx-c-function/wiki/Nginx-Cache-Data-via-nginx-c-function). Это для разработчиков, которые любят хозяина сервера c. https://github.com/Taymindis/nginx-c-function
Массивы C # можно индексировать только с целыми типами. Так пишете хэш-таблицу сопоставления строк с указателями функций.
Он также может быть стоит посмотреть на объект в Python, Lua и других языков скрипт для размещения их во время выполнения в программе c: части кода С, то можно работать со скриптовым языком.
АЛТ'лы, у некоторых людей код расширения языка сценариев в с. Затем они могут иметь скорость &ампер; низкий уровень доступа к C в их сценарии.
Вы собираетесь найти, рано или поздно, что с помощью динамически типизированный язык сценарий фразеологизмы - как функцию eval(), и размытые линии между функцию и имя функции, и код, который зависит от ДОЦ массивы в C можно, но в конечном итоге больно.