63

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.

jscs
  • 63,694
  • 13
  • 151
  • 195
wantobegeek
  • 1,665
  • 3
  • 19
  • 19
  • 3
    I don't think it's possible, but could you elaborate on why you want to do this? Even if you can, it sounds dangerous. – Thomas Owens Jul 13 '09 at 10:29
  • what are you trying to achieve and what OS/compiler are u targeting? – AndersK Jul 13 '09 at 10:54
  • 7
    It will be so special when he answers. – John Saunders Jul 13 '09 at 19:44
  • @thomas owens,now tha i have elaborated on why i want to do this...can u help???ppl have been suggesting me to use a lookup table but that wont work since the user is free to name the function and the file whatever he wants.After he enters the name I should be able to call the function with the name that he has entered after having included the file which has the same name as the function name entered. – wantobegeek Jul 15 '09 at 05:01

12 Answers12

42

C does not support this kind of operation (languages that have reflection would). The best you're going to be able to do is to create a lookup table from function names to function pointers and use that to figure out what function to call. Or you could use a switch statement.

Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
38

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.

Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 4
    +1 for "if you're trying to solve a problem with this, I'm sure there is a way better suited to your task." – John Carter Jul 13 '09 at 10:40
  • slightly older post, but is there any way to do something like you did above, but where the number of parameters is different? I can't think of a way. – gnychis Jan 31 '14 at 23:56
  • @gnychis: Do you mean the number of parameters is not set until runtime? (Say, one call to a callback would have 3 arguments, another would have 4?) – GManNickG Feb 01 '14 at 04:34
36

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;
  }   
Blub
  • 13,014
  • 18
  • 75
  • 102
Falaina
  • 6,625
  • 29
  • 31
15

It's not possible in pure C, however you may be able to play tricks with dlls. Put all the functions you want to select from into a dll, then use dlsym (or GetProcAddress on Windows, or whatever other API your system offers) to get the function pointer by name, and call using that.

This doesn't work on some platforms, either because they don't have dlls at all, or because like Symbian the functions in the dll can't be accessed by name at runtime, only by number.

Be aware that if your user tricks you into selecting a function which doesn't have the right parameters for the call you want to make, then your program will go wrong. C really isn't designed to cope with this kind of thing.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Or whichever dynamic library API is available on your OS. :) – GManNickG Jul 13 '09 at 10:39
  • 2
    As a side note; if you want to access functions using `dlsym`, they don't actually have to be in a dll; you can link your executable in a suitable fashion and treat the executable image as a dll for this purpose. – Williham Totland Jul 13 '09 at 10:43
  • onebyone, yes, that's what `dlopen(NULL,` achieves (you also have to tell the linker you want your symbols available to the dynamic loader, using `-rdynamic` with gcc). – caf Aug 18 '09 at 13:50
15
#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");
}
Sean Bright
  • 118,630
  • 17
  • 138
  • 146
5

I tried this solution : to open the executable as a dynamic library with dlopen().

It's working fine on my Linux Ubuntu but unfortunately not on my embedded ARM target. I don't know why, if it's depend of the glibc or the version of libdl maybe.

On my ARM target, the message is clear: "./test8: cannot dynamically load executable".

Anyway, the code I used is this one.

I compiled with:

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;
}
Erwan
  • 61
  • 1
  • 2
3

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.

neuro
  • 14,948
  • 3
  • 36
  • 59
2

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.

Community
  • 1
  • 1
Jose Quinteiro
  • 469
  • 4
  • 9
1

If I understand your question correctly you want to use late binding to call a C function. That is not something your normally can do in C. Symbolic names (like names of functions) are not stored in the code generated by the C compiler. You could probably let the compiler emit symbols, and then use those to perform the late binding, but the technique would vary from compiler to compiler and probably not be worth the hazzle.

Languages like C# and Java supports reflection making it easy to perform late binding.

Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
1

Is this what you are trying:

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;
}
Naveen
  • 74,600
  • 47
  • 176
  • 233
0

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.

0

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

Oktaheta
  • 606
  • 5
  • 21