Call a function named in a string variable in C

2019-01-04 09:24发布

I want to call a function using a variable. Is that possible in C?

Actually, what I want to do is, get the function name from the user and store it in a variable. Now I want to call the function that has its name stored. Can anyone tell me how this can be done in C?

I want to develop an AI game engine for a two player game. Two programs with no main function that implement the logic for winning the game will be fed to the game engine. Let me be clear that the program names will be same as that of the primefunctions in the program that implement the logic for winning the game.

So when the user enters the name of first and second players, I can store them in 2 different variables. Now, since the primefunction names are same as that of the program names, I intend to call the functions with variables containing the prog names.

标签: c function
11条回答
Juvenile、少年°
2楼-- · 2019-01-04 09:41

While this isn't exactly a practical solution, I bet you could certainly call a function by a string by having a program read in it's own executable and parse the symbols table. The symbol table should contain the name of the function as well as it's first instruction address. You could then place this address in a function pointer variable and call it.

I think I may try and whip this up.

EDIT: Please no one ever write real code like this, but here is how you can call a function using a string for a Linux ELF binary with an intact symbols table (requires 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;
  }   
查看更多
淡お忘
3楼-- · 2019-01-04 09:42

Introducing the Nginx-c-function. It is a NGINX module that allow you to link your .so(c/c++) application in server context and call the function of .so application in location directive. You can implement nginx share memory data cache via nginx c function(https://github.com/Taymindis/nginx-c-function/wiki/Nginx-Cache-Data-via-nginx-c-function). This is for developer who love to host c server. https://github.com/Taymindis/nginx-c-function

enter image description here

查看更多
forever°为你锁心
4楼-- · 2019-01-04 09:46

I just tried Steve Jessop's approach using a statically-linked library as suggested by Williham Totland in comments and it turned out to be non-trivial.

Firstly, you'll find a bunch of places on the Web (including Wikipedia) which will tell you that the way to open your main program as a library is to call dlopen(3) like this dlopen(NULL, 0). This will not work for glibc because a binding flag has to be specified, as the man page clearly states:

One of the following two values must be included in flag:
RTLD_LAZY
Perform lazy binding. Only resolve symbols as the code...
RTLD_NOW
If this value is specified, or the environment variable...

I don't think which one you choose matters here because you're going to link all the symbols from the static library into your executable.

This brings us to the next problem. The linker will not include the symbols from your static library in your executable because they're not referenced. The way to force the GNU linker to include the symbols anyway is -Wl,--whole-archive path/to/static/lib.a -Wl,--no-whole-archive as that answer describes. The way to force the Mac OS X linker to include all the symbols from your static library is -Wl,-force_load path/to/static/lib.a.

查看更多
叼着烟拽天下
5楼-- · 2019-01-04 09:46

C arrays can only be indexed with integral types. So write a hash table mapping strings to function pointers.

It might also be worth looking at the facility in Python, Lua, other script languages to embed their run-time in a C program: parts of the C code can then be manipulated with the scripting language.

Alt'ly, some people code script language extensions in C. Then they can have the speed & low level access of C in their scripts.

You are going to find, sooner or later, that using dynamically typed script language idioms - like eval(), and the blurry line between function and function name, and code that depends on assoc arrays - in C is possible but ultimately painful.

查看更多
Bombasti
6楼-- · 2019-01-04 09:48

The best you can do is something like this:

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

This uses numbers instead of strings to identify the functions, but doing it with strings is simply a matter of converting the string into the proper function.

What are you doing, exactly? If just for curiosity, here you go, but if you're trying to solve a problem with this, I'm sure there is a way better suited to your task.

Edit

In concern with your edit, you will want to go with onebyone's answer, for sure.

You want your user's to build dynamic libraries (thats a shared object [.so] in Linux, and a dynamic link library [.dll] in Windows).

Once you do that, if they provide you with the name of their library, you can ask the operating system to load that library for you, and request a pointer to a function within that library.

查看更多
\"骚年 ilove
7楼-- · 2019-01-04 09:52

As said by others, it is true that C has no reflection mechanism. But you can achieve this sort of behaviour by using dynamic loaded library/shared object. In fact you can load a dynamic library and then you can call the functions in the dll/so with their name ! It is not C and OS specific but that's the way. It use dlopen on Linux and LoadLibrary on windows. You can find libraries that do the work for you like gtk/glib.

查看更多
登录 后发表回答