views:

594

answers:

7

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?

+3  A: 

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
+7  A: 

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.

Hope this helps, Best regards, Tom.

tommieb75
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, size_t functionListLength = sizeof(functionList) / sizeof(puzzleFoo);` for example.
Mike D.
@Mike D: That's an excellent suggestion! :) Thanks for your input! :)
tommieb75
+2  A: 

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.

+3  A: 

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  A: 

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
+2  A: 

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.

ScaryAardvark
+3  A: 

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
Nice code.. I'm sure I've seen it before... Oh yes.. On the linux man page for dlopen() :)
ScaryAardvark
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