Friday, July 16, 2010

AUTOLOAD in C

Recently, I attended a Perl training on Advanced Perl
(actually, I attended just to kill time, and most of the
subject matter being covered there was known to me :) )
and when learning perl modules, I came across AUTOLOAD

In brief, AUTOLOAD is like a default in a module, when one
tries to invoke a function which is not implemented in the
module, perl calls the function with name AUTOLOAD
passing the function name.
Having AUTOLOAD gives rise to interesting uses - atleast in
Perl. One can handle multiple calls in AUTOLOAD giving the
impression that there are lot of functions defined in the
library - especially useful when method names are like
get_a() get_b() get_c() etc.

I was trying to implement this in C, and its pretty
straight forward. All we need is a dynamic library and a
a default handler - which we shall call catch_all()
The only difference here would be, the responsibility
to call catch_all() will be with the invoker of the lib
(maybe this can be hidden behind neat APIs)

Lets create a library with a function and a catch_all

/* sotest.c */
#include
#include

void name1 ()
{
printf ("\n SOTEST: called %s\n", __FUNCTION__);
}
/* this is our AUTOLOAD method, which takes 1 param - name */
void catch_all (char *name)
{
/* If someone tried to call method hello() in this module */
if (0 == strcmp(name, "hello")) {
printf ("\n SOTEST: called hello()\n");
} else
printf ("\n SOTEST: %s, %s()\n", __FUNCTION__, name);
}

make lib: gcc -c -fPIC sotest.c; gcc -shared -fPIC -o libsotest.so sotest.o

Now for the invoker:

/* callso.c */
#include
#include

int main (int argc, char *argv[])
{
void *handle, (*fp)() = NULL, (*gfp)() = NULL;
char *fname = NULL;

if (argc <>\n", argv[0]);
exit (1);
}
fname = argv[1]; /* The function to be invoked in the lib */

handle = dlopen("./libsotest.so", RTLD_LOCAL | RTLD_LAZY);

fp = dlsym(handle, fname);
if (fp) {
fp(); /* If the named function exists, invoke it */
} else {
gfp = dlsym(handle, "catch_all");
gfp ? gfp(fname): 0; /* call the default handler if not */
}

return 0;
}

compile: gcc -L. callso.c -lsotest -ldl # Assume current dir, set LD_LIBRARY_PATH

Simple !. isn't it ? :)