C varargs - va_copy проблемы
Я пишу функцию в C, которая принимает переменное количество аргументов.
size_t myprintf(char *fmt, ...);
Все идет нормально. Я решил, что лучше сделать все правильно и сделать версию, которая принимает переменные аргументы, и другую версию, которая принимает va_list
,
size_t myprintf(char *fmt, ...);
size_t myvprintf(char *fmt, va_list args);
Не так сложно сделать. Кроме my_vprintf()
необходимо отправить его args
из двух разных функций (сначала snprintf()
с длиной 0, чтобы определить, сколько места нам нужно, затем sprintf()
после того, как мы выделили столько места). Я делаю это с va_copy
,
size_t myvprintf(char *fmt, va_list args)
{
va_list args2;
va_copy(args, args2);
// do stuff with args2
va_end(args2);
// do more stuff with args
}
Это все прекрасно и модно, но C99 немного плохо реализован. Я хотел бы, если возможно, чтобы мой код работал и в C89, и работал с как можно большим количеством компиляторов и на как можно большем количестве платформ. У меня в настоящее время есть это после #include <stddef.h>
но перед любым кодом:
#ifndef va_copy
# ifdef __va_copy
# define va_copy(a,b) __va_copy(a,b)
# else /* !__va_copy */
# define va_copy(a,b) ((a)=(b))
# endif /* __va_copy */
#endif /* va_copy */
Я склонен полагать, что ((a)=(b))
ненадежно, и что я, возможно, должен использовать memcpy()
или что-то подобное, но это все еще на уровне "Если вы не поддерживаете C99, я надеюсь, что это работает", а не "Если вы не поддерживаете C99, никогда не бойтесь" (это то, что я хочу). Есть ли хороший способ обойти это ограничение? Я видел несколько решений - va_list
функции, которые едят один аргумент и рекурсивно, передавая va_list
дважды, чтобы сделать две отдельные копии и т. д. - но я не знаю, насколько хорошо они будут работать (и рекурсивное решение не будет так хорошо, если я просто хочу позвонить vsnprintf()
теперь это будет?)
Итак, я обращаюсь к вам, пользователь Stackru. Что еще я могу сделать, чтобы обеспечить совместимость с C89, или пользователи без va_copy
а также __va_copy
(по общему признанию, немного и далеко) просто придется засосать это и взять это?
1 ответ
(a)=(b)
ненадежен Даже проходя два va_list
is (публичная функция будет простой оболочкой), так как va_list может выглядеть примерно так:
typedef __va_list_impl va_list[1];
для которого выполнение va_arg для одного будет модифицировать другое (кажется, я помню, что Solaris использует такие вещи, ах, окна регистрации...). К сожалению, я не знаю ни одного способа сделать то, что вы хотите.