Как я могу вызвать (не определить) функцию с переменным числом аргументов в C? - PullRequest
4 голосов
/ 23 июля 2010

Есть ли способ сделать этот код короче?

long call_f(int argc, long *argv) {
  switch (argc) {
    case 0:
      return f();
      break;
    case 1:
      return f(argv[0]);
      break;
    case 2:
      return f(argv[0], argv[1]);
      break;
    case 3:
      return f(argv[0], argv[1], argv[2]);
      break;
    case 4:
      return f(argv[0], argv[1], argv[2], argv[3]);
      break;
    // ...
  }
  return -1;
}

Ответы [ 6 ]

3 голосов
/ 23 июля 2010

Нет, нет хорошего способа сделать это. Посмотреть здесь: http://c -faq.com / переменные аргументы / handoff.html

Вы можете написать макрос с вставкой токена, чтобы скрыть это поведение, но этот макрос будет не проще, чем этот код, поэтому его стоит писать, только если у вас есть несколько функций, таких как f (), где в противном случае вам пришлось бы дублировать этот оператор case .

3 голосов
/ 23 июля 2010

Я не знаю, как вы можете сделать ваш код короче, но я видел эту строку в вашем коде:

 return f();

При следующих вызовах функции f кажется, что f - это функцияэто принимает переменное количество аргументов.

Вы можете прочитать в википедии , что:

В функциях Variadic должен быть хотя бы один именованный параметр, например,

char *wrong(...);

не допускается в C.

Исходя из этого, может быть, оператор return f(); вызывает у вас проблемы?

1 голос
/ 05 сентября 2012

Я опубликую здесь тот же ответ, который я написал на дублирующем вопросе , но вы должны взглянуть на обсуждение там:

Что такое libffi?

Некоторые программы могут не знать во время компиляции, какие аргументы должны быть переданы функции.Например, интерпретатору можно сообщить во время выполнения о количестве и типах аргументов, используемых для вызова данной функции.«libffi» может использоваться в таких программах для обеспечения моста между программой-интерпретатором и скомпилированным кодом.

Библиотека «libffi» предоставляет переносимый высокоуровневый программный интерфейс для различных соглашений о вызовах.Это позволяет программисту вызывать любую функцию, указанную в описании интерфейса вызова во время выполнения.

FFI означает Интерфейс внешней функции.Интерфейс сторонней функции - это популярное имя интерфейса, которое позволяет коду, написанному на одном языке, вызывать код, написанный на другом языке.Библиотека 'libffi' действительно предоставляет только самый нижний, машинно-зависимый уровень полнофункционального интерфейса сторонних функций.Слой должен существовать выше «libffi», который обрабатывает преобразования типов для значений, передаваемых между двумя языками.

«libffi» предполагает, что у вас есть указатель на функцию, которую вы хотите вызвать, и что вы знаете число и типыаргументов для его передачи, а также тип возвращаемого значения функции.


Исторический фон

libffi, первоначально разработанный Энтони Грин (пользователь SO: anthony-green ), вдохновлено библиотекой Gencall из Silicon Graphics.Gencall был разработан Джанни Мариани, который затем работал в SGI, с целью разрешения вызовов функций по адресу и создания кадра вызова для конкретного соглашения о вызовах.Энтони Грин усовершенствовал идею и распространил ее на другие архитектуры, соглашения о вызовах и открытый доступ к libffi.


Вызов pow с помощью libffi

#include <stdio.h>
#include <math.h>
#include <ffi.h>

int main()
{
  ffi_cif     call_interface;
  ffi_type    *ret_type;
  ffi_type    *arg_types[2];

  /* pow signature */
  ret_type = &ffi_type_double;
  arg_types[0] = &ffi_type_double;
  arg_types[1] = &ffi_type_double;

  /* prepare pow function call interface */
  if (ffi_prep_cif(&call_interface, FFI_DEFAULT_ABI, 2, ret_type, arg_types) == FFI_OK)
  {
    void *arg_values[2];
    double x, y, z;

    /* z stores the return */
    z = 0;

    /* arg_values elements point to actual arguments */
    arg_values[0] = &x;
    arg_values[1] = &y;

    x = 2;
    y = 3;

    /* call pow */
    ffi_call(&call_interface, FFI_FN(pow), &z, arg_values);

    /* 2^3=8 */
    printf("%.0f^%.0f=%.0f\n", x, y, z);
  }

  return 0;
}

Я думаюcan assert libffi - это портативный способ сделать то, что я просил, вопреки утверждению Антти Хаапалы, что такого способа нет.Если мы не можем назвать libffi переносимой технологией, учитывая, насколько она портирована / реализована в компиляторах и архитектурах и какой интерфейс соответствует стандарту C, мы тоже не можем назвать C или что-то еще переносимым.

Информация и история извлечены из:

https://github.com/atgreen/libffi/blob/master/doc/libffi.info

http://en.wikipedia.org/wiki/Libffi

1 голос
/ 24 июля 2010

На самом деле есть метод для вызова функции во время выполнения, если вы знаете ее соглашение о вызовах и какие параметры она получает. Это, однако, лежит за пределами стандартной области языка C / C ++.

Для х86 ассемблера:

Предполагая следующее:

  1. Вы знаете, как подготовить все параметры для вашей функции в твердом буфере, точно так, как они будут упакованы в стек.
  2. Ваша функция не принимает / не возвращает объекты C ++ по значению.

Тогда вы можете использовать следующую функцию:

int CallAnyFunc(PVOID pfn, PVOID pParams, size_t nSizeParams)
{
    // Reserve the space on the stack
    // This is equivalent (in some sense) to 'push' all the parameters into the stack.
    // NOTE: Don't just subtract the stack pointer, better to call _alloca, because it also takes
    // care of ensuring all the consumed memory pages are accessible
    _alloca(nSizeParams);

    // Obtain the stack top pointer
    char* pStack;
    _asm {
        mov pStack, esp
    };

    // Copy all the parameters into the stack
    // NOTE: Don't use the memcpy function. Because the call to it
    // will overwrite the stack (which we're currently building)
    for (size_t i = 0; i < nSizeParams; i++)
        pStack[i] = ((char*) pParams)[i];

    // Call your function
    int retVal;
    _asm {
        call pfn
        // Most of the calling conventions return the value of the function (if anything is returned)
        // in EAX register
        mov retVal, eax
    };

    return retVal;
}

Вам может потребоваться настроить эту функцию в зависимости от используемого соглашения о вызове

0 голосов
/ 23 июля 2010

Должен ли f принимать переменное число указателей на long? Можете ли вы переписать его, чтобы принять массив и счетчик?

0 голосов
/ 23 июля 2010

Вы можете проверить мой ответ на:

Лучший способ сохранить список va_list для последующего использования в C / C ++

Что, кажется, работает, но пугает людей. Он не гарантированно кроссплатформенный или переносимый, но, по крайней мере, кажется работоспособным на нескольких платформах. ;)

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