0

How can I call a function on the runtime? I'm wondering if I can do that without using an if or a switch to call it,

if(arg[1] == something) //Now what I'm looking for
      call_function;

Below is what I'm looking for, which is calling the function print using arg?

#include <stdio.h>

void print()
{

    printf("print function called\n");
}

int main(int argc, char *argv[])
{
    argv[1] to call the print function 
}

So then I can call it like this,

 ./a.out print
BenMorel
  • 34,448
  • 50
  • 182
  • 322
user2628572
  • 945
  • 1
  • 8
  • 14
  • 7
    See http://stackoverflow.com/questions/1118705/call-a-function-named-in-a-string-variable-in-c . –  Dec 30 '13 at 20:50

3 Answers3

4

This isn't trivial or automatic. Function names only exist in the language, not in the executing program. You have to supply a mapping from a suitable string constant to the function yourself (or use some existing mechanism like dlsym on Posix). You can refer to the function via a function pointer:

typedef void (void_function)(void);

extern void print(void);
extern void foo(void);
extern void bar(void);

void_function my_little_functions[] = { print, foo, bar };


int main(int argc, char * argv[])
{
    if (strcmp(argv[1], "print") == 0) { my_little_functions[0](); }
    // ...
}

This is for illustration only; instead of a sequence of conditional statements, you should implement some suitable, function-pointer-valued associative lookup structure.

(In C++, this would be a std::unordered_map<std::string, void_function *>. In C, you have to find your own.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 2
    "Function names only exist in the language, not in the executing program" - if you `strip`. If you don't, then non-`static` function names must *somehow* be preserved. `dlsym(handle, argv[1])` works pretty well, for starters. –  Dec 30 '13 at 20:54
  • @H2CO3: Non-standard :-) Dynamic linking is (unfortunately) not part of the language standard. Posix makes additional guarantees, at least in some way. I'm pretty sure that you can ultimately customize and rewrite symbol export names, though, so the connection between source code and names you can put into `dlsym` is somewhat tentative. – Kerrek SB Dec 30 '13 at 20:56
  • @H2CO3: In fact, scratch that. I think my position on this is that a vendor of a shared library binary will simply have to *tell* you a list of names together with function types which can be used with `dlsym`. That telling *may* happen in the shape of a header file, but it doesn't have to. – Kerrek SB Dec 30 '13 at 20:58
  • I know it's non-standard, and you're right that it's definitely better practice to inform users of the function names, but "it's not possible" is **simply wrong.** Anyway, OP didn't write that he needed a solution that works on any OS, under all circumstances, without relying on any external library... –  Dec 30 '13 at 20:59
  • @H2CO3: Luckily for me, I don't appear to be saying "it's not possible" :-) – Kerrek SB Dec 30 '13 at 21:01
  • Yes, not literally, however, "You have to supply a mapping from a suitable string constant to the function yourself" strongly suggests that. –  Dec 30 '13 at 21:01
  • 1
    @H2CO3: I suppose you can outsource that mapping to `dlsym`, if you were so inclined... – Kerrek SB Dec 30 '13 at 21:02
  • Well, yes... but don't you see my point? It is more than likely that OP does not **have to** fall back to implementing his own string-function map himself, because such a mapping already exists. And why not use already existing tools instead of reinventing the wheel? `dlopen()` is nearly universal on Unixes, and *even Windows* has `GetProcAddress()`. –  Dec 30 '13 at 21:04
2

If you are not looking for a "pure C" solution, then chances are you can use APIs of your OS to do the mapping for you. Under POSIX (this compiles and runs):

#include <stdio.h>
#include <dlfcn.h>

void foo(void)
{
    printf("foo\n");
}

void bar(void)
{
    printf("bar\n");
}


void baz(void)
{
    printf("baz\n");
}

int main(int argc, char *argv[])
{
    void *hndl = dlopen(NULL, RTLD_LAZY);
    void (*fn)(void) = dlsym(hndl, argv[1]);

    if (fn) {
        fn();
    } else {
        printf("Function %s() not found\n", argv[1]);
    }

    return 0;
}
0

Well, actually there is a way...

At runtime you could write a .c file containing the functions definitions and in main you would write the call to the function named in argv[1]. Then you could invoke a compiler (gcc-linux or cl-windows or whatever) (via system(command)) to generate an executable. Then you invoke that executable (via system(command)) and voilà job done.

What? I didn't say it's a practical way, I just said it's a way.


Proof that this actually works:

#include <stdio.h>
#include <stdlib.h>

const char file_includes[] = "#include <stdio.h>\n";
const char file_functions[] = 
  "void print() {\n"
  "    printf(\"print function called\\n\");\n"
  "}\n"
  "void print2() {\n"
  "    printf(\"second print function called\\n\");\n"
  "}\n";

const char file_main_prolog[] = "int main() {\n";
const char file_main_epilog[] = "  return 0;\n}\n";


int main(int argc, char *argv[]) {
  if (argc < 2) {
    printf("invalid program arguments\n");
    return 1;
  }

  FILE *fout = fopen("function_call.c", "w");

  fprintf(fout, "%s", file_includes);
  fprintf(fout, "%s", file_functions);
  fprintf(fout, "%s", file_main_prolog);
  fprintf(fout, "  %s();\n", argv[1]);
  fprintf(fout, "%s", file_main_epilog);

  fclose(fout);

  system("gcc -o function_call function_call.c");
  system("./function_call");

  return 0;
}

This works under linux with gcc installed. You can run this program with the argument print or print2 and you will get print function called or second print function called respectively.

bolov
  • 72,283
  • 15
  • 145
  • 224