Call a function named in a string variable in C

CFunction

C Problem Overview


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 Solutions


Solution 1 - C

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.

Solution 2 - C

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.

Solution 3 - C

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

Solution 4 - C

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

Solution 5 - C

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.

Solution 6 - C

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

Solution 7 - C

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.

Solution 8 - C

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.

Solution 9 - C

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.

Solution 10 - C

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

Solution 11 - C

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.

Solution 12 - C

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionwantobegeekView Question on Stackoverflow
Solution 1 - CBlair ConradView Answer on Stackoverflow
Solution 2 - CGManNickGView Answer on Stackoverflow
Solution 3 - CFalainaView Answer on Stackoverflow
Solution 4 - CSean BrightView Answer on Stackoverflow
Solution 5 - CSteve JessopView Answer on Stackoverflow
Solution 6 - CErwanView Answer on Stackoverflow
Solution 7 - CneuroView Answer on Stackoverflow
Solution 8 - CJose QuinteiroView Answer on Stackoverflow
Solution 9 - CMartin LiversageView Answer on Stackoverflow
Solution 10 - CNaveenView Answer on Stackoverflow
Solution 11 - ChoohooView Answer on Stackoverflow
Solution 12 - COktahetaView Answer on Stackoverflow