#c #macros
#c #макросы
Вопрос:
Я попытался определить макрос, функционирующий как показано ниже. Вызов 1 не имеет проблем, но вызов 2 вызвал ошибку компилятора, поскольку 3-й аргумент недоступен. Как определить макрос, который поддерживает как вызов 1, так и вызов 2?
#define RDF_LOG(dbglevel, fmt, ...) (rdfDBG(dbglevel, " " fmt, __VA_ARGS__))
void rdfDBG(int dbglevel, const char *fmt, ...) { /* printf debug message */ }
RDF_LOG(kERROR, "Fail to open file %sn", pinfile); /* Call 1 */
RDF_LOG(kERROR, "Insufficient Memoryn"); /* call 2 , compiler -> error: expected expression before ')' token */
Ответ №1:
Вы получаете дополнительную запятую во втором расширении макроса, потому что у вас есть безусловная запятая после fmt
в определении макроса.
Удаление fmt
параметра из определения макроса, похоже, устраняет проблему; затем строка формата становится частью __VA_ARGS__
:
#define RDF_LOG(dbglevel, ...) (rdfDBG(dbglevel, " " __VA_ARGS__))
void rdfDBG(int dbglevel, const char *fmt, ...) { /* printf debug message */ }
RDF_LOG(kERROR, "Fail to open file %sn", pinfile); /* Call 1 */
RDF_LOG(kERROR, "Insufficient Memoryn");
Это расширяется до:
void rdfDBG(int dbglevel, const char *fmt, ...) { }
(rdfDBG(kERROR, " " "Fail to open file %sn", pinfile));
(rdfDBG(kERROR, " " "Insufficient Memoryn"));
Кстати, похоже " "
, что предполагается, что формат должен быть строковым литералом (и моя измененная версия сохраняет это). Вы уверены, что хотите это сделать? Хотя это редко, может быть полезно иметь строку нелитерального формата.
Ответ №2:
Расширения GCC
GCC имеет расширение для обработки этого (обратите внимание на пропущенную запятую перед ...
):
Неверно (ссылки __VA_ARGS__
, которые не разрешены в расширении GCC):
#define RDF_LOG(dbglevel, fmt ...) (rdfDBG(dbglevel, " " fmt, __VA_ARGS__))
Правильно (не ссылается __VA_ARGS__
):
#define RDF_LOG(dbglevel, fmt...) (rdfDBG(dbglevel, " " fmt))
void rdfDBG(int dbglevel, const char *fmt, ...) { /* printf debug message */ }
enum { kERROR };
void x(const char *pinfile);
void x(const char *pinfile)
{
RDF_LOG(kERROR, "Fail to open file %sn", pinfile);
RDF_LOG(kERROR, "Insufficient Memoryn");
}
Вы можете сказать, что я не использую расширение GCC — потому что я использую некоторые компиляторы, которые не являются GCC.
Существует также второй (специфичный для GCC) механизм, упомянутый Адамом в его комментарии:
#define RDF_LOG(dbglevel, fmt, ...) (rdfDBG(dbglevel, " " fmt, ## __VA_ARGS__))
void rdfDBG(int dbglevel, const char *fmt, ...) { /* printf debug message */ }
enum { kERROR };
void x(const char *pinfile);
void x(const char *pinfile)
{
RDF_LOG(kERROR, "Fail to open file %sn", pinfile);
RDF_LOG(kERROR, "Insufficient Memoryn");
}
Стандарт C99
В противном случае вам придется использовать стандартный механизм C99:
#define RDF_LOG(dbglevel, ...) (rdfDBG(dbglevel, " " __VA_ARGS__))
void rdfDBG(int dbglevel, const char *fmt, ...) { /* printf debug message */ }
RDF_LOG(kERROR, "Fail to open file %sn", pinfile); /* Call 1 */
RDF_LOG(kERROR, "Insufficient Memoryn");
Это в основном обманывает или обходит проблему для этого контекста. В общем случае для C99 требуется запятая и хотя бы один аргумент.
Комментарии:
1. Ваш первый пример не работает. Вы уверены, что не думаете о расширении GCC, где, если вы добавляете
##
к__VA_ARGS__
и нулевые аргументы совпадают с многоточием, то это удаляет конечную запятую? Например.#define RDF_LOG(fmt, ...) foo(fmt, ##__VA_ARGS__)
приводит кRDF_LOG("foo")
==>foo("foo")
.