C varargs - va_copy проблемы - PullRequest
       14

C varargs - va_copy проблемы

6 голосов
/ 11 августа 2009

Я пишу функцию в 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(), не так ли?).

Итак, я обращаюсь к вам, пользователь StackOverflow. Могу ли я сделать что-нибудь еще, чтобы обеспечить совместимость с C89, или пользователям без va_copy и __va_copy (по общему признанию, мало и далеко) просто придется смириться с этим и взять его?

1 Ответ

3 голосов
/ 11 августа 2009

(a)=(b) ненадежно. Даже если передать два va_list (публичная функция будет простой оболочкой), va_list может выглядеть примерно так:

typedef __va_list_impl va_list[1];

, для которого выполнение va_arg на одном из них приведет к изменению другого (мне кажется, я помню, что Solaris использует такие вещи, ах, окна регистрации ...). К сожалению, я не знаю ни одного способа сделать то, что вы хотите.

...