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 использует такие вещи, ах, окна регистрации...). К сожалению, я не знаю ни одного способа сделать то, что вы хотите.

Другие вопросы по тегам