Хороший многословный макрос (C99) - PullRequest
0 голосов
/ 25 февраля 2012

Я ищу написать то, что я представляю себе, довольно распространенный макрос.Я хочу эмулировать повторяющиеся параметры "-v" во многих программах POSIX, определив набор макросов следующей формы:

#define V1(str, ...) if(optv >= 1){printf("%s: "str,prog,__VA_ARGS__);}

int main(int argc, char* argv[])
{
  // ... stuff ...
  int i = 1;
  V1("This contains a variable: %d\n",i);
}

// Output:
// ./program: This contains a variable: 1

, где optv подсчитывает количество параметров "-v", найденных вкомандная строка и prog содержат имя программы (не показано).Это хорошо работает, но проблема в том, что у меня есть для использования переменной.V1("Output") сгенерирует ошибку компилятора.Я всегда мог использовать V1("Output%s",""), но должно быть более чистое решение.

Ответы [ 4 ]

5 голосов
/ 25 февраля 2012

Препроцессор GNU C имеет специальную функцию , которая позволяет удалить запятую, когда нет аргументов, заполняющих часть переменной, добавив оператор вставки токена ## к __VA_ARGS__:

#define V1(str, ...) if(optv < 1); else printf("%s: "str,prog, ## __VA_ARGS__)

В качестве альтернативы, если вы хотите сохранить полную совместимость с C99, вы можете включить параметр многострочного формата в многоточие, но в этом случае вам также потребуется реорганизовать код, поскольку вы хотите включить дополнительныйprog параметр между строкой формата и переменными.Примерно так может работать:

#define V1(...) if(optv < 1); else myprintf(prog, __VA_ARGS__)
int myprintf(const char *prog, const char *fmt, ...)
{
    // Print out the program name, then forward the rest onto printf
    printf("%s: ", prog);

    va_list ap;
    va_start(ap, fmt);
    int ret = vprintf(fmt, ap);
    va_end(ap);

    return ret;
}

Затем V1("Output") расширяется до myprintf(prog, "Output") без использования каких-либо расширений компилятора, отличных от C99.

EDIT

Также обратите внимание, что я инвертировал условие if в макросе из-за некоторых странных проблем, которые могут возникнуть, если вы вызываете макрос внутри оператора if без фигурных скобок - см. этот FAQ дляподробное объяснение.

1 голос
/ 25 февраля 2012

Вероятно, вам следует написать небольшую вспомогательную функцию, чтобы вы могли выполнять работу чисто:

extern void vb_print(const char *format, ...);

#define V1(...)  do { if (optv >= 1) vb_print(__VA_ARGS__); } while (0)

Я предполагаю, что и optv, и prog являются глобальными переменными.Они будут в заголовке (вы бы не записали их в самих программах, не так ли?).

Функция может быть такой:

#include <stdio.h>
#include <stdarg.h>

extern const char *prog;

void vb_print(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    printf("%s:", prog);
    vprintf(format, args);
    va_end(args);
}

Там нет ракетостроения,Вы можете настроить систему на свое усмотрение, позволяя выбирать, где записывается информация, сбрасывать вывод, обеспечивать наличие новой строки в конце и т. Д.

1 голос
/ 25 февраля 2012

Почему бы вам не использовать 2 разных макроса для каждого уровня детализации;тот, который печатает сообщение и переменную, и тот, который просто печатает сообщение?

0 голосов
/ 25 февраля 2012

Попробуйте это:

#define V1X(str, ...) if(optv >= 1) {printf("%s: "str,prog,__VA_ARGS__);} else
#define V1(...) V1X(__VA_ARGS__,0)

Я считаю, что это решает проблему, которую вы описали, а else в конце решает другую проблему.

...