Можно ли создать массив обобщенных указателей на функции c? - PullRequest
3 голосов
/ 25 февраля 2020

Я пишу интерфейсный слой в разделяемую библиотеку, которую мы открываем через dlopen / dlsym. Параметры функций могут изменяться в зависимости от того, скомпилирована ли библиотека для системы A против системы B.

У меня была мысль, что вместо того, чтобы делать кучу typedefs, я мог бы попытаться создать массив всех указатели на функции возвращаются из dlsym и затем в конечном итоге используют VA_ARGS для передачи данных. Это также позволило бы мне иметь дело с расширяемостью - если параметры библиотеки изменятся, нам просто нужно будет обновить вызывающие функции, вместо приведенных ниже typedefs. По сути, я хочу изменить что-то вроде этого:

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();
}

на это:

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]();
}

Возможно ли это? Я знаю, что массив пустот незаконен. Я не могу использовать uintptr_t вместо него, так как они предназначены для указателей на данные (а не функции). Есть ли другие варианты, кроме создания больших # define для каждой конфигурации и выполнения оригинального способа?

Ответы [ 2 ]

2 голосов
/ 26 февраля 2020

Объявите массив как массив указателей на функции. На самом деле тип указателя на функцию не имеет значения.

void (*funcArray[FUNC_MAX])();

Тогда перед вызовом необходимо преобразовать указатели на функции правильного типа:

((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])();
1 голос
/ 26 февраля 2020

Есть два основных способа добиться выбора во время выполнения сигнатуры типа вызываемой функции.

  1. Есть где-нибудь оператор switch, который включает целочисленный код ( разработан вами) с указанием типа функции. Все случаи переключения - это вызовы, которые по-разному приводят указатель функции и вызывают его с разными аргументами.

  2. Используйте библиотеку для динамического построения вызовов функций, например libffi ( MIT-подобная лицензия) или libffcall , (программное обеспечение GNU, лицензированное по GPL).

Вариант 2 требует более требовательного кодирования и может быть слишком общим для такой ситуации , Целью этих библиотек является поддержка динамических c языков, способных вызывать C библиотеки. Динамический c язык может использовать libffi или libffcall в качестве серверной части для перевода описания функции в объект, который можно использовать для ее вызова.

Опция 2 намекает на опцию 3: Напишите вашу программу или его часть, на языке более высокого уровня, который имеет хорошую FFI и использует это для достижения гибких вызовов в разделяемые библиотеки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...