source

C의 문자열 변수에 명명된 함수를 호출합니다.

factcode 2022. 9. 3. 13:11
반응형

C의 문자열 변수에 명명된 함수를 호출합니다.

변수를 사용하여 함수를 호출하고 싶다.그게 C에서 가능한가요?

사실 제가 하고 싶은 것은 사용자로부터 함수명을 얻어 변수에 저장하는 것입니다.이제 이름이 저장된 함수를 호출합니다.C에서 어떻게 할 수 있는지 말해 줄 사람?

나는 2인용 AI 게임 엔진을 개발하고 싶다.게임의 승리 논리를 구현하는 주요 기능이 없는 프로그램 2개가 게임 엔진에 공급된다.프로그램 이름은 게임에서 이기는 논리를 구현하는 프로그램의 주요 기능과 동일하다는 것을 명확히 해 두겠습니다.

그래서 사용자가 1번과 2번 플레이어의 이름을 입력하면 2개의 다른 변수에 저장할 수 있습니다.이제 주요 함수명은 프로그램 이름과 같기 때문에 프로그램 이름을 포함하는 변수를 사용하여 함수를 호출하려고 합니다.

이것이 꼭 실용적인 해결책은 아니지만, 프로그램을 실행 파일로 읽어 심볼 표를 해석함으로써 함수를 문자열로 호출할 수 있을 것입니다.기호 테이블에는 함수의 이름과 첫 번째 명령 주소가 포함되어야 합니다.그런 다음 이 주소를 함수 포인터 변수에 배치하여 호출할 수 있습니다.

서둘러야 할 것 같아요

편집: 절대 이와 같은 실제 코드를 작성하지 마십시오. 단, 여기에서는 Linux ELF 바이너리 스트링을 사용하여 함수를 호출할 수 있습니다(명예훼손 필요).

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

C는 이러한 조작을 지원하지 않습니다(반사가 있는 언어).함수 이름에서 함수 포인터까지 룩업 테이블을 만들고 이를 사용하여 호출할 함수를 결정하는 것이 최선입니다.또는 switch 문을 사용할 수도 있습니다.

dlopen()을 사용하여 실행 파일을 동적 라이브러리로 여는 방법을 시도했습니다.

Linux Ubuntu에서는 정상적으로 동작하고 있습니다만, 안타깝게도 임베디드 ARM 타겟에서는 동작하지 않습니다.glibc에 의존하는지 libdl 버전에 의존하는지 모르겠어요.

ARM 타깃에서는 ".test8: cannot load dynamically executable"이라는 메시지가 나타납니다.

어쨌든 제가 사용한 코드는 이것입니다.

컴파일 대상:

gcc test8.c -ldl -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;
}

Williham Totland가 댓글로 제시한 정적 링크 라이브러리를 사용하여 Steve Jessop의 접근방식을 시도해보니 사소한 것이 아니었다.

(Wikipedia (3에 전화하는 .dlopen(NULL, 0)해야 하기 man 페이지에는 다음과 같이명시되어 있습니다.

다음 두 값 중 하나를 플래그에 포함해야 합니다.
RTLD_게으름뱅이
느린 바인딩을 수행합니다.★★★★★★★★★★★★★★★...
RTLD_NOW
이 값이 지정되거나 환경 변수가 지정되면...

여기서는 어떤 것을 선택해도 상관없습니다.스태틱 라이브러리의 모든 기호를 실행 파일로 링크하기 때문입니다.

이것이 다음 문제로 이어집니다.링커는 참조되지 않으므로 정적 라이브러리의 기호를 실행 파일에 포함하지 않습니다.GNU 링커에 심볼을 포함하도록 강제하는 방법은 다음과 같습니다.-Wl,--whole-archive path/to/static/lib.a -Wl,--no-whole-archive그 답변이 기술한 바와 같이. X 시키는 방법은 Mac OS X 입니다.-Wl,-force_load path/to/static/lib.a.

할 수 있는 최선은 다음과 같습니다.

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

이것은 함수를 식별하기 위해 문자열 대신 숫자를 사용하지만 문자열을 사용하여 수행하는 것은 문자열을 적절한 함수로 변환하는 문제일 뿐입니다.

정확히 뭐 하는 거야?단지 호기심이라면, 여기 있습니다. 하지만 만약 당신이 이 문제를 해결하려고 한다면, 나는 당신의 일에 더 적합한 방법이 있다고 확신합니다.

편집

당신의 편집에 대해 걱정되신다면, 반드시 한 사람의 답변으로 진행하시기 바랍니다.

사용자가 동적 라이브러리(Linux에서는 공유 개체 [.so], Windows에서는 동적 링크 라이브러리 [.dll])를 구축하도록 합니다.

이렇게 하면 라이브러리의 이름을 알려준 경우 운영체제에 해당 라이브러리를 로드하도록 요청하고 해당 라이브러리 내의 함수에 대한 포인터를 요청할 수 있습니다.

순수 C에서는 불가능하지만 dll로 장난을 칠 수 있습니다.dll에 dll을 사용합니다.dlsym(또는GetProcAddressWindows 또는 시스템에서 제공하는 다른 API)를 사용하여 함수 포인터를 이름으로 가져와 호출합니다.

이것은 일부 플랫폼에서는 동작하지 않습니다.이것은 dll이 전혀 없기 때문이거나 Symbian과 같이 Dll의 함수는 런타임에 이름으로만 액세스 할 수 없고 숫자로만 액세스 할 수 없기 때문입니다.

유저가, 발신하는 콜에 적절한 파라메타가 없는 함수를 선택하도록 속이면, 프로그램이 잘못되는 것에 주의해 주세요.C는 정말 이런 일에 대처할 수 있도록 설계되지 않았다.

다른 사람들이 말했듯이 C는 반사 메커니즘이 없는 것이 사실이다.그러나 동적으로 로드된 라이브러리/공유 개체를 사용하면 이러한 동작을 수행할 수 있습니다.실제로 동적 라이브러리를 로드하고 dll/so의 함수를 이름으로 호출할 수 있습니다.C와 OS에 고유한 것은 아니지만 그렇게 해야 합니다.Linux에서는 dlopen을 사용하고 Windows에서는 LoadLibrary를 사용합니다.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와 같은 언어는 리플렉션을 지원하므로 지연 바인딩을 쉽게 수행할 수 있습니다.

C 어레이는 통합 유형으로만 인덱싱할 수 있습니다.따라서 해시 테이블 매핑 문자열을 함수 포인터에 씁니다.

또한 Python, Lua 및 기타 스크립트 언어의 기능을 검토하여 실행 시간을 C 프로그램에 포함시키는 것이 좋습니다.C 코드의 일부는 스크립트 언어로 조작할 수 있습니다.

스크립트 언어 확장자를 C로 코드화하는 사람도 있습니다.그러면 스크립트에서 C의 속도와 낮은 수준의 액세스를 얻을 수 있습니다.

조만간 C에서 eval()과 같은 동적 형식의 스크립트 언어 숙어, 함수와 함수 이름 사이의 흐릿한 줄, assoc 배열에 따라 달라지는 코드를 사용하는 것이 가능하지만 궁극적으로 고통스럽다는 것을 알게 될 것입니다.

Nginx-c 함수를 소개합니다.서버 컨텍스트에서 .so(c/c++) 응용 프로그램을 링크하고 위치 지시에서 .so 응용 프로그램의 함수를 호출할 수 있는 NGINX 모듈입니다.nginx 공유 메모리 데이터 캐시는 nginx c 함수(https://github.com/Taymindis/nginx-c-function/wiki/Nginx-Cache-Data-via-nginx-c-function)를 통해 구현할 수 있습니다.이것은 c 서버를 호스트 하는 것을 좋아하는 개발자를 위한 것입니다.https://github.com/Taymindis/nginx-c-function

여기에 이미지 설명 입력

언급URL : https://stackoverflow.com/questions/1118705/call-a-function-named-in-a-string-variable-in-c

반응형