Передача аргументов переменных из макроса в функцию, ожидающую va_list - PullRequest
0 голосов
/ 29 мая 2020
#define exampleA(buf, args...) \
        exampleB(buf, ##args); \
    }

#endif

работает там, где объявлением функции exampleB является exampleB (char * buf, ...). Но мне нужно изменить объявление как exampleB (char * buf, va_list args). Как можно соответствующим образом изменить марку?

Ответы [ 2 ]

4 голосов
/ 29 мая 2020

Вы не можете. Для того, чтобы создать va_list, вам понадобится функция Variadi c. Не существует переносимого средства для создания объекта va_list из списка аргументов.

Довольно часто функции Variadi c пишутся парами: внешняя функция использует средства в <stdarg.h> для построения a va_list, и он передает этот список непосредственно функции, которая выполняет эту работу. Вы видите, что в стандартной библиотеке ввода-вывода, например: printf / vprintf, fprintf / vfprintf, snprintf / vsnprintf, scanf / vscanf, et c .

И вот что вы должны сделать для своей проблемы: запишите exampleB как va_list форму exampleA.

Вот, например, полная реализация fprintf извлечена из библиотеки FreeBSD, которая может служить моделью этого стиля:

int
fprintf(FILE * __restrict fp, const char * __restrict fmt, ...)
{
    int ret;
    va_list ap;

    va_start(ap, fmt);
    ret = vfprintf_l(fp, __get_locale(), fmt, ap);
    va_end(ap);
    return (ret);
}

Как видно из этого фрагмента, vfprintf_l (который принимает аргумент локали) на самом деле является реализацией ; все остальные *printf функции являются простыми оболочками. (printf, например, вызывает vfprintf_l с передачей stdin и аргумента языкового стандарта.) Это позволяет избежать обхода нескольких оболочек за один вызов, хотя современные оптимизирующие компиляторы обычно могут выполнять это исключение автоматически.

(См. lib / libc / stdio / fprintf. c для полного файла в зеркале исходного репозитория FreeBSD на Github.)

Говоря о переносимости, #define exampleA(buf, args...) - это расширение G CC, которое не переносимо для большинства реализаций C (хотя Clang также реализует его). Вы должны использовать __VA_ARGS__ в соответствии со стандартом C:

#define exampleA(buf, ...) \
        exampleB(buf, ## __VA_ARGS__)

Еще лучше, если вы можете, заменить другое расширение G CC (, ##) на более новый стандарт __VA_OPT__:

#define exampleA(buf, ...) \
        exampleB(buf __VA_OPT__(,) __VA_ARGS__)
2 голосов
/ 29 мая 2020

Я не думаю, что это возможно, как вы это себе представляете. Давайте рассмотрим функцию exampleX(char *,...): передаваемые вами аргументы должны где-то жить, и скажем, они помещаются в стек. Макросы va_list знают об этом и могут получить к ним доступ оттуда. В тот момент, когда вы покидаете эту функцию, нельзя предполагать, что аргументы больше находятся в стеке.
Таким образом, вы можете не написать вспомогательную функцию, например va_list getArgs(char*x,...) { /* use va_start and return the va_list */ }, и передать эту va_list на exampleB.

Единственное, что я могу придумать, - это написать функцию трамплина, такую ​​как

void callExampleB(char * buf, ...) {
  va_list ap;
  va_start(ap, buf);
  exampleB(buf, ap);
  va_end(ap);
}

, и вызвать ее в вашем макросе.

...