Заполнение va_list - PullRequest
       9

Заполнение va_list

25 голосов
/ 12 июня 2009

Есть ли способ создать va_list с нуля? Я пытаюсь вызвать функцию, которая принимает va_list в качестве параметра:

func(void **entry, int num_args, va_list args, char *key); 

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

void stupid_func(void **entry, char *key, int num_args, ...) {
    va_list args;
    va_start(args, num_args);

    func(entry, num_args, args, key);

    va_end(args);
}

Есть ли лучший способ? Я не могу изменить подпись func.

Ответы [ 4 ]

14 голосов
/ 12 июня 2009

Это плохая идея , потому что абстракция va_list предназначена для того, чтобы скрыть некоторые мрачные специфичные для компилятора / архитектуры детали относительно указателей стека, а что нет. И это в значительной степени связано с областью действия функции после инициализации. Если вы свернете стек и ссылаетесь на предыдущие кадры va_args, то все может пойти плохо. Вы можете передать их, но ...

ожидаемые ошибки

См .: http://lists.freebsd.org/pipermail/freebsd-amd64/2004-August/001946.html

Также ознакомьтесь с man (3) va_copy и друзьями для более безопасного обращения с va_args и их передачи.

ИМХО, материал va_args не очень аккуратен. В прошлом я имел дело с этим, инициализируя структуры / непрозрачные указатели в куче, а затем используя арифметику указателей для обработки данных. Но это взлом и зависит от обстоятельств.

6 голосов
/ 12 июня 2009

Я понимаю и согласен с предупреждениями Эйдена - va_list и друзья опасны, поскольку они скрывают соглашения о низком уровне вызовов. Но ... в этой ситуации я думаю, что у вас нет другого выбора. Поместите функцию static ... в файл .c, чтобы никто больше ее не увидел, вроде прокси-функции для функции, которую вам нужно вызвать, протестируйте ее и все готово. Просто убедитесь, что вы не выставляете вариационные аргументы в цепочке вызовов.

5 голосов
/ 13 июня 2009

Ваша идея прокси-функции, которая создает va_list, является правильным способом сделать это. Нет необходимости, чтобы этот прокси имел общедоступную область. Однако, если вы можете обнаружить, что прокси уже существует. Например, во многих реализациях библиотеки sprintf() является просто прокси для vsprintf().

Если вы не хотите связать свой код с конкретным компилятором и целевой платформой, лучшего способа нет. Имена, определенные в <stdarg.h>, предназначены для обеспечения переносимого и согласованного интерфейса для поддержки доступа к спискам переменных аргументов. Единственный переносимый способ реализации и использования функций с переменными значениями - через этот интерфейс.

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

3 голосов
/ 29 декабря 2016

Ваш stupid_func является совершенно корректным кодом C, иначе как бы вы предположительно вызвали vprintf и подобные функции?

Библиотека glib широко использует эти оболочки. Сама спецификация C99 имеет нечто подобное в примерах. Выдержка из раздела 7.19.6.8 :

Ниже показано использование функции vfprintf в общей процедуре сообщения об ошибках.

#include <stdarg.h>
#include <stdio.h>
void error(char *function_name, char *format, ...)
{
    va_list args;
    va_start(args, format);
    // print out name of function causing error
    fprintf(stderr, "ERROR in %s: ", function_name);
    // print out remainder of message
    vfprintf(stderr, format, args);
    va_end(args)
}
...