Хороший многословный макрос (C99)

Я ищу написать то, что я представляю, довольно распространенный макрос. Я хочу эмулировать повторяющиеся опции "-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 ответа

Решение

Препроцессор 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.

РЕДАКТИРОВАТЬ

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

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

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

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);
}

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

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

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

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

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