Is it possible to create an array of generic function pointers?

Viewed 226

I'm writing an interface layer to a shared library we are opening via dlopen/dlsym. The parameters to the functions may change depending on whether the library is compiled for system A vs system B.

I had a thought that instead of doing a bunch of typedefs, I could try to create an array of all of the function pointers returned from dlsym and then eventually use VA_ARGS to pass in the data. This would also allow me to deal with extensibility-- if the library parameters change, we'd just need to update the calling functions, instead of the typedefs below, too. Basically, I'm looking to change something like this:

typedef int* (*foo_t)(int, int);
typedef int* (*bar_t)(int, int, char*, int);
typedef void (*baz_t)(void);

void GetStuff()
{
  void *handle = dlopen("/my/cool/libLibrary.so", RTLD_LAZY);
  foo_t foo = dlsym(handle, "TheFooFunction");
  bar_t bar = dlsym(handle, "TheBarFunction");
  baz_t baz = dlsym(handle, "TheBazFunction");

  foo(1, 2);
  bar(3, 4, "cool", 5);
  baz();
}

into this:

enum funcs
{
  FUNC_FOO = 0,
  FUNC_BAR = 1,
  FUNC_BAZ = 2,
  FUNC_MAX
};

void funcArray[FUNC_MAX]; // Not legal - looking for a way to do something like this...

char *functionNames[FUNC_MAX] =
{
   [FUNC_FOO] = "TheFooFunction",
   [FUNC_BAR] = "TheBarFunction",
   [FUNC_BAZ] = "TheBazFunction"
};

void GetStuff()
{
  void *handle = dlopen("/my/cool/libLibrary.so", RTLD_LAZY);
  for (int i = 0; i < FUNC_MAX; i++)
  {
    funcArray[i] = dlsym(handle, functionNames[i]);
  }

  funcArray[FUNC_FOO](1, 2);
  funcArray[FUNC_BAR](3, 4, "cool", 5);
  funcArray[FUNC_BAZ]();
}

Is this possible? I know the array of voids is illegal. I can't use uintptr_t in it's place, since those are for data (not function) pointers. Are there any other options other than creating big #define's for each configuration and doing it the original way?

2 Answers

Declare the array as an array of function pointers. Actually the type of function pointer does not matter.

void (*funcArray[FUNC_MAX])();

Then right before you call you have to convert your function pointers to the correct type:

((int *(*)(int, int))funcArray[FUNC_FOO])(1, 2);
((int *(*)(int, int, char*, int))funcArray[FUNC_BAR])(3, 4, "cool", 5);
((void (*)(void))funcArray[FUNC_BAZ])();

There are two main ways to achieve run-time selection of the type signature of a function being called.

  1. Have a switch statement somewhere which switches on an integer code (designed by you) indicating the function type. The switch cases are all calls which cast the function pointer differentlly and call it with different arguments.

  2. Use a library for constructing funcction calls dynamically, such as libffi (MIT-like license) or libffcall, (GPL-licensed GNU software).

Option 2 requires more demanding coding and may be overly general for a situation like this. The purpose of these libraries is to support dynamic languages being able to call C libraries. A dynamic language can use libffi or libffcall as a back end for translating the description of a function into an object which can be used to call it.

Option 2 hints at an option 3: Write your program, or a portion of it, in a higher level language that has good FFI, and leverage that to achieve the flexible calls into the shared libraries.

Related