Заполнение va_list
Есть ли способ создать 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
.
Ответы
Ответ 1
Это плохая идея, потому что абстракция va_list заключается в том, чтобы скрыть некоторые специфические детали компилятора/архитектуры grim, относящиеся к указателям на стек, а что нет. И это в значительной степени связано с областью функций после инициализации. Если вы намотаете стек и ссылаетесь на предыдущие кадры va_args вне сферы действия, все может пойти не так. Вы можете передать их, но...
ожидать ошибки
Смотрите: http://lists.freebsd.org/pipermail/freebsd-amd64/2004-August/001946.html
Также проверяйте человека (3) va_copy и друзей для более безопасного обращения с va_args и прохождения их.
ИМХО, материал va_args не очень опрятен. Раньше я занимался этим, инициализируя структуры/непрозрачные указатели в куче, а затем используя арифметику указателя для работы с данными. Но это хак и зависит от обстоятельств.
Ответ 2
Я понимаю и соглашаюсь с предупреждениями Aiden - va_list
, а друзья опасны, так как они скрывают соглашения о вызове низкого уровня. Но... в этой ситуации я думаю, что у вас нет другого выбора. Поместите функцию static ...
в файл .c
, чтобы никто не мог ее увидеть, вроде прокси-сервера для функции, которую нужно вызвать, проверить ад из нее и сделать это. Просто убедитесь, что вы не раскрываете переменные аргументы в цепочке вызовов.
Ответ 3
Ваша идея прокси-функции, которая создает va_list
, - это правильный способ сделать это. Нет необходимости в том, чтобы этот прокси-сервер имел общий охват. Однако, если вы обнаружите, что прокси-сервер уже существует. Например, во многих реализациях библиотек sprintf()
является просто прокси-сервером для vsprintf()
.
Если вы не хотите привязать свой код к определенному компилятору и целевой платформе, нет лучшего способа. Имена, определенные в <stdarg.h>
, предназначены для обеспечения портативного и последовательного интерфейса для поддержки доступа к спискам переменных параметров. Единственный переносимый способ реализации и использования вариационных функций - через этот интерфейс.
Тем не менее, возможно, что вы можете пожертвовать переносимостью, дублируя кадр вызова в массиве и вручную создавая va_list
, который правильно ссылается на него. Результат никогда не будет переносимым.
Ответ 4
В cocoawithlove есть полезное сообщение в блоге, в котором предлагается способ подделать va_list - он блокирует вас до компилятора и специфическое поведение платформы, о чем свидетельствуют другие хорошие ответы здесь.
Ответ 5
Твой stupid_func
- это действительно действующий C-код, иначе как вы могли бы называть vprintf
и подобные функции?
glib
library используют эти обертки широко. Сама спецификация 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)
}