Подтвердить что ты не робот

Динамически вызывает функцию C с аргументом varargs

Я программирую на C в сторонней библиотеке (в HP/Mercury Loadrunner), которая разрешает список аргументов размера переменной varargs для одной из его функций. Я хочу вызвать эту функцию, но я не знаю, сколько аргументов у меня будет.

Существует функция, сделанная одним из моих предшественников, которая работает несколько, но проблема заключается в том, что эта функция предполагает наихудший сценарий (более 3000 аргументов) и ручные коды для этого.

Чтобы подсветить, здесь (начало) кода. Вызываемая функция web_submit_data(). Он будет отправлять HTTP-данные из набора данных формы. Эта реализация возникла при работе с динамически сгенерированными формами с произвольным числом полей. (Очистили справедливый бит от оригинала, который также вручную закодировал индексы.)


web_submit_data_buffer_gazillion_items( const char *bufferName, const char *bufferValue)
{
    const int size = 129;
    int i = 0;
    int j = 11;

    web_submit_data(&bufferName[i++ * size], //"some form"
                &bufferName[i++ * size], //"Action=https://blah.blah/form");
                &bufferName[i++ * size], //"Method=POST");
                &bufferName[i++ * size], //"TargetFrame=");
                &bufferName[i++ * size], //"RecContentType=text/html");
                &bufferName[i++ * size], //"Referer=https://blah.blah/index.html");
                &bufferName[i++ * size], //"Snapshot=t1.inf");
                &bufferName[i++ * size], //"Mode=HTML");
                ITEMDATA,  // missing in action: indexes 8 through 10
                &bufferName[j * size],&bufferValue[j++ * size], ENDITEM, 
                &bufferName[j * size],&bufferValue[j++ * size], ENDITEM, 
                &bufferName[j * size],&bufferValue[j++ * size], ENDITEM, 
..
(repeat the last 3 lines ad nauseum)
..
                &bufferName[j * size],&bufferValue[j++ * size], ENDITEM,
                &bufferName[j * size]);  
}

Теперь я нашел внешнюю библиотеку, которая может работать (http://www.dyncall.org), но я бы скорее не а) полностью зависал процессор и б) попытаться обучить Loadrunner обложению во внешних источниках.

Изменить: Исходная функция использовала жестко кодированные индексы вместо использования переменной. Может все же вернуться к этому, если окажется слишком непредсказуемым. Однако, поскольку я вряд ли буду запускать это с помощью другого компилятора или оборудования/ОС, я сомневаюсь, что это действительно того стоит.

Также: у меня нет контроля над реализацией web_submit_data(). Так что просто нажатие проблемы на один уровень не собирается сокращать ее.

Еще одно замечание: спецификация для web_submit_data() использует константу LAST для обозначения конца списка аргументов. Исходная реализация не использует его. Предположительно, callsite делает..

4b9b3361

Ответ 1

В CamelBones я использую libffi для вызова objc_msgSend(), который является функцией varargs. Работает с удовольствием.

Ответ 2

Аргументы переменной длины - это всего лишь указатель на кучу упакованных данных, которые передаются требуемой функции. Ответственность за эту упакованную информацию несет вызываемая функция.

Безопасным для архитектуры способом сделать это является использование макросов va_list (об этом упоминался n-alexander), иначе вы можете столкнуться с проблемами с тем, как различные типы данных заполняются в памяти.

Правильный способ разработки функций varargs состоит в том, чтобы фактически иметь две версии, которые принимают "...", которая, в свою очередь, извлекает va_list и передает ее функции, которая принимает va_list. Таким образом, вы можете динамически строить аргументы, если вам нужно, и вместо этого можете вызывать версию функции va_list.

Большинство стандартных функций ввода-вывода имеют версии varargs: vprintf для printf, vsprintf для sprintf... вы получаете эту идею. Посмотрите, реализует ли ваша библиотека функцию с именем "vweb_submit_data" или что-то в этом роде. Если они этого не сделают, напишите им и скажите им, чтобы они исправили свою библиотеку.

3000 строк одной и той же вещи (даже если она вызвана препроцессором) заставляет меня съеживаться

Ответ 3

Так как обычно не проблема передавать больше аргументов функции, принимающей переменные аргументы, чем функция ожидает (см. сноску # 1), вы можете сделать что-то вроде следующего:

// you didn't give a clear specification of what you want/need, so this 
// example may not be quite what you want as I've had to guess at
// some of the specifications. Hopefully the comments will make clear
// what I may have assumed.
//
// NOTE:  while I have compiled this example, I have not tested it,
//        so there is a distinct possiblity of bugs (particularly
//        off-by-one errors). Check me on this stuff, please.

// I made these up so I could compile the example
#define ITEMDATA ((char const*) NULL)
#define ENDITEM  ((char const*) 0xffffffff)

void web_submit_data_wrapper( const char*bufferName, 
                              const char* bufferValue, 
                              size_t headerCount,       // number of header pointers to pass (8 in your example)
                              size_t itemStartIndex,    // index where items start in the buffers (11 in your example)
                              size_t itemCount,         // number of items to pass (unspecified in your example)
                              size_t dataSize )         // size of each header or item (129 in your example)
{
    // kMaxVarArgs would be 3000 or a gazillion in your case

    // size_t const kMaxVarArgs = 20;  // I'd prefer to use this in C++
    #define kMaxVarArgs (20)

    typedef char const* char_ptr_t;
    typedef char_ptr_t char_ptr_array_t[kMaxVarArgs];

    char_ptr_array_t varargs = {0};

    size_t idx = 0;

    // build up the array of pararmeters we'll pass to the variable arg list

    // first the headers
    while (headerCount--) {
        varargs[idx++] = &bufferName[idx * dataSize];
    }

    // mark the end of the header data
    varargs[idx++] = ITEMDATA;

    // now the "items"
    while (itemCount--) {
        varargs[idx++] = &bufferName[itemStartIndex * dataSize];
        varargs[idx++] = &bufferValue[itemStartIndex * dataSize];
        varargs[idx++] = ENDITEM;

        ++itemStartIndex;
    }

    // the thing after the last item 
    // (I'm not sure what this is from your example)
    varargs[idx] = &bufferName[itemStartIndex * dataSize];

    // now call the target function - the fact that we're passing more arguments
    //  than necessary should not matter due to the way VA_ARGS are handled 
    //  but see the Footnote in the SO answer for a disclaimer

    web_submit_data( 
        varargs[0],
        varargs[1],
        varargs[2],

        //... ad nasuem until

        varargs[kMaxVarArgs-1]
        );

}

Сноска № 1: Если вы думаете о том, как действуют макросы в stdargs.h, это становится ясным. Однако я не утверждаю, что этот метод будет соответствовать стандартам. Фактически, в недавней истории ответы stackoverflow, которые я опубликовал, где я, я сделал это выражение об отказе, на самом деле были признаны нестандартными (обычно всегда бдительным litb). Поэтому используйте этот метод на свой страх и риск и проверьте, проверьте, проверьте).

Ответ 4

Нет портативного способа создания списка аргументов для функции переменных аргументов в C во время выполнения. Есть несколько зависимых от реализации трюков, библиотека dyncall, которую вы нашли, выглядит как хорошая и, вероятно, более переносимая, чем большинство.

Ответ 5

Примечание: код уже зависит от компилятора (хотя, возможно, и не зависит от процессора), потому что вызов web_submit_data предполагает, что подвыражения аргумента в вызове процедуры оцениваются в порядке слева направо, но Язык C оставляет порядок оценки аргументов неуказанным.

См. ссылку: http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_value

Таким образом, возможно, не переносное решение не сделает вещи значительно хуже для вас.

Ответ 6

Можете ли вы перестроить свой код, чтобы это не было необходимо? Возможно, вы можете взять входящий буфер и сделать его более детерминированным:

struct form_field
{
  char[FIELD_NAME_MAX] name;
  char[FIELD_VALUE_MAX] val;
};

web_submit_data_buffer_gazillion_items( const char *bufferName, const char *bufferValue)
{
    /*
      loop over bufferName somehow, either with a known size or terminating record,
      and build an array of form_field records
    */
    //loop
    {
      // build array of records
    }


    web_submit_data(record_array, array_len);

}

Извините, это не могло быть больше фальшивым - моя жена позвонила мне на завтрак.: -)

Ответ 7

Напишите один раз с препроцессором и никогда не оглядывайтесь назад.

#define WEB_SUBMIT_BUFFER(name, val)         \
    do {                                     \
        const int size = 129;                \
        int i = 0;                           \
        int j = 11;                          \
        web_submit_data(&(name)[i++ * size], \
                        &(name)[i++ * size], \
        /* etc ad nauseum */                 \
    } while (0)

Или, если число аргументов фиксировано для каждого конкретного вызова, напишите script, чтобы сгенерировать препроцессор, чтобы скрыть, насколько это отвратительно этот вызов.

#define WEB_SUBMIT_BUFFER_32(name, val)      \
    do {                                     \
        const int size = 129;                \
        int i = 0;                           \
        int j = 11;                          \
        web_submit_data(&(name)[i++ * size], \
                        &(name)[i++ * size], \
        /* 32 times */                       \
    } while (0)
#define WEB_SUBMIT_BUFFER_33(name, val) ...
#define WEB_SUBMIT_BUFFER_34(name, val) /* etc */

Ответ 8

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

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

Кроме того, я не вижу, как web_submit_data() знает, сколько аргументов было передано - я не вижу в конце счетчика или окончательного аргумента дозорного. Но я думаю, что ваш пример может быть именно таким - пример, который может не иметь полных, точных деталей. С другой стороны, проблема web_submit_data() в любом случае, верно?

Ответ 9

Есть два способа передать переменное количество аргументов: функции, которая принимает "..." или функции, которая принимает va_list.

Вы не можете динамически определять количество аргументов для интерфейса "...", но вы должны иметь возможность сделать это для va_list. Google для va_start, va_end и va_list.

Ответ 10

Я знаю, что это старый поток, но я просто наткнулся на него. Правильный способ обработки данных формы переменной длины в LoadRunner заключается в использовании web_custom_request(). Вы создаете структуру пары name | value для переменной длины аргументов как строку и передаете ее как часть функции.

Запишите один вызов как web_custom_request(), и структура строки аргумента для пар name | value станет очевидной. Просто используйте любые функции обработки строки C, которые вы хотите построить, и укажите ее как часть списка аргументов для web_custom_request().