Хороший многословный макрос (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
в конце исправлена еще одна проблема.