8

I saw this question in one of the C puzzles!! Is this really possible?

How can I call a function, given its name as a string? is it possible to use string that is read with scanf be used directly to call a function?

i already have thought of if(strcmp(str,"string"))then call the function.

but is there any other approach?

mipadi
  • 398,885
  • 90
  • 523
  • 479
Vijay
  • 65,327
  • 90
  • 227
  • 319
  • yes Qt uses it for implementing signalslot – yesraaj Feb 02 '10 at 14:51
  • 1
    The foundation of the issue is the fact that compilers don't put the names of functions into executable (except maybe for debug versions). There is no requirement in the C language for a compiler to retain the names of functions. – Thomas Matthews Feb 02 '10 at 18:28
  • 1
    What is a function? Impossible to say after the code has been compiled and linked. The compiler can do whatever the hell it wants: create new functions, delete old functions (inlining), etc. – Andreas Bonini Feb 03 '10 at 08:41
  • IIRC, compilers can keep a non-inlined version of a function around if there's a chance its pointer is used. – Mike D. Feb 03 '10 at 13:51
  • possible duplicate of [Call a function named in a string variable in C](http://stackoverflow.com/questions/1118705/call-a-function-named-in-a-string-variable-in-c) – Ciro Santilli OurBigBook.com Aug 27 '15 at 14:21

7 Answers7

13

Since no mention of what is a function, nor its parameters, I would imagine it something like this:

typedef void (*foo)();
struct puzzleFoo{
   char *function_name;
   foo *fn;
};

Create a lookup table based on the structure, using a string parameter

struct puzzleFoo *Lookup(const char *function_name);

Then iterate through an array/list looking for the puzzleFoo's function_name and execute the function pointer called fn.

t0mm13b
  • 34,087
  • 8
  • 78
  • 110
  • 2
    That `char*` in `puzzleFoo` should be a `const char*`. Also, you can define a macro to create a `puzzleFoo` for you: `#define FN_ENTRY(funcPtr) { #funcPtr, &funcPtr }` Then you just say `puzzleFoo functionList[] = { FN_ENTRY(fooA), FN_ENTRY(fooB) }; size_t functionListLength = sizeof(functionList) / sizeof(puzzleFoo);` for example. – Mike D. Feb 03 '10 at 13:54
  • @Mike D: That's an excellent suggestion! :) Thanks for your input! :) – t0mm13b Feb 03 '10 at 14:35
10

With POSIX.1-2001 you should use dlopen() and dlsym(). On Windows use GetModuleHandleEx() and GetProcAddress().

Here's an example verbatim from the manual that loads the function named "cos" from the math library and determines the cosine of 2.0:

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

int
main(int argc, char **argv)
{
   void *handle;
   double (*cosine)(double);
   char *error;

   handle = dlopen("libm.so", RTLD_LAZY);
   if (!handle) {
       fprintf(stderr, "%s\n", dlerror());
       exit(EXIT_FAILURE);
   }

   dlerror();    /* Clear any existing error */

   /* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
      would seem more natural, but the C99 standard leaves
      casting from "void *" to a function pointer undefined.
      The assignment used below is the POSIX.1-2003 (Technical
      Corrigendum 1) workaround; see the Rationale for the
      POSIX specification of dlsym(). */

   *(void **) (&cosine) = dlsym(handle, "cos");

   if ((error = dlerror()) != NULL)  {
       fprintf(stderr, "%s\n", error);
       exit(EXIT_FAILURE);
   }

   printf("%f\n", (*cosine)(2.0));
   dlclose(handle);
   exit(EXIT_SUCCESS);
}
Matt Joiner
  • 112,946
  • 110
  • 377
  • 526
  • Ha, our answers don't differ much. You've provided some extra stuff about namemangling, but I know that noobs like code samples, giving function names is never enough. – Matt Joiner Feb 03 '10 at 15:20
4

You could be sneaky:

if (strcmp (fn, "function1") == 0) function1();
if (strcmp (fn, "function2") == 0) function2();
if (strcmp (fn, "function3") == 0) function3();

or you could use dlopen and dlsym (or equivalent) depending on your execution environment but these aren't standard C so that might not be an option.

That's about all I've got other than incredibly non-portable stuffing around with the executable file or designing hideous macros (which would most likely boil down to the collection of if statements shown above anyway, or a function pointer array of some sort).

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
3

If the function is available in a shared library, you may be able to load the shared library at run time and get access to the symbol table, which would be convertible to function names and pointers. As for making sure the function calling signature is correct, you're on your own.

VxWorks does this for its built-in shell.

Mike D.
  • 1,457
  • 7
  • 7
3

Yes, sometimes.

Under Linux, you could use dlopen() to either open up a shared library containing the function you want or even access the current executable and locate your function using the dlsym()

On Windows, you would typically call LoadLibrary() and GetProcAddress() respectively.

If the symbol tables for the libraries in question have been stripped or, in some cases, if the methods are static/private then you will not be able to access them using this approach.

Also, don't forget, if you're library is written in C++ then you may have to contend with name mangling. You'll need to understand the mangling method employed by the compiler used to in the face of this.

Matt Joiner
  • 112,946
  • 110
  • 377
  • 526
ScaryAardvark
  • 2,855
  • 4
  • 30
  • 43
2

Its depend of what function you'd want to call.
If this is a function from DLL or other type of exported binary -- of course you can.
There is an ordinary API for this.
If this function is not exported and was compiled in executable -- of course not, as such information as func names are truncated.

user204724
  • 160
  • 9
2

I wrote this off the top of my head - no guarantee that it will even compile, but it's probably pretty close. Good enough for an interview.

#include <stdio.h>
#include <unistd.h>
char *program = "#include<stdio.h>\
\
void Func1() { printf("hi\n"); }\
void Func2() { printf("hello\n"); }\
void Func3() { printf("hey\n"); }\
main() {\
";
char *program1 = "}\n";

static in ContainsFunction(char *func)
{
    char *match = strstr(func, program);
    if (!match || match - program < 5)
        return 0;
    int len = strlen(func);
    return strcmp(match-5, program) == 0 && match[len + 1] == '(' && isalnum(func[len-1]);
}

static void Compile(char *prog)
{
    pid_t pid = fork();
    if (pid == 0) {
        execl("/usr/bin/gcc", "gcc", prog, (char *)0);
    }
    else if (pid < 0) { printf("fork fail\n"); }
    else {
        pid_t ws = wait();
    }
 }

 static void Execute(char *prog)
 {
    pid_t pid = fork();
    if (pid == 0) {
        execl(prog, prog, (char *)0);
    }
    else if (pid < 0) { printf("fork fail\n"); }
    else {
        pid_t ws = wait();
    }
 }

static void CallFunction(char *funcname)
{
    FILE *fp = fopen("foo.c", "w");
    fputs(program, fp);
    fprintf(fp, "\t%s();\n", funcname);
    fputs(fp, program1);
    fclose(fp);

    Compile("foo.c");
    Execute("a.out");
}

int main()
{
    char funcname[8192]; /* too small, fail */
    puts("Who ya gonna call?");
    gets(funcname);
    if (ContainsFunction(funcname)) { CallFunction(funcname)); }
    else { printf("fail - no function %s\n", funcname); }
}    
plinth
  • 48,267
  • 11
  • 78
  • 120