Я действительно рекомендую написать для этого два отдельных макроса, точно так же, как вы написали бы две функции с разными именами для двух сигнатур в C. аргумент по умолчанию.)
Тем не менее, есть две возможности достичь того, чего вы хотите.
C11 _Generic
выбор
Ключевое слово _Generic
появилось в C11. Это позволяет расширять макросы switch
-подобным образом в соответствии с типом аргумента; У Роберта Гэмбла есть хорошее введение.
Вы хотите различать два случая: первый аргумент — строка, а первый аргумент — целое число. Недостатком является то, что в _Generic
строковый литерал обрабатывается не как char *
или const char *
, а как char[size]
. Например, "%d"
— это char[3]
.
В вашем случае мы можем обойти это, рассматривая строку как все, что не является целым числом. Компилятор отсортирует все нестроковые и нецелочисленные аргументы позже. Так:
#define PRINT(fmt, ...) \
_Generic(fmt, \
int: syslog(fmt, __VA_ARGS__), \
default: syslog(3, fmt, __VA_ARGS__))
Есть и недостатки: у вас не может быть вызова с одним аргументом, потому что это оставит запятую в вызове. (##__VA_ARGS__
gcc решает эту проблему.) И _Generic
ключевое слово еще не получило широкого распространения; это решение сделает ваш код крайне непереносимым.
Строковый хак самоанализа
Обычные макросы C99 не имеют информации о своем типе. Однако код C может сделать предположение. Вот пример, который проверяет, является ли аргумент макроса строковым литералом:
#define PRINT(sev, ...) \
if (#sev[0] == '"') syslog(3, sev, __VA_ARGS); \
else syslog(sev, __VA_ARGS__);
Это работает - почти. Компилятор, вероятно, скомпилирует постоянное условие и сгенерирует код только для одной из ветвей. Но он все равно будет анализировать ветки, и мертвая ветка будет иметь неправильную сигнатуру функции, которая будет генерировать предупреждения.
Вы можете обойти это, написав интерфейсную функцию с переменным числом аргументов на C. Вот пример, который работает:
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#define HEAD(X, ...) X
#define STR_(x) #x
#define STR(x) STR_(x)
#define PRINT(...) \
msg(*STR(HEAD(__VA_ARGS__)) == '"', __VA_ARGS__)
int msg(int dflt, ...)
{
va_list va;
int sev = 3;
const char *fmt;
va_start(va, dflt);
if (!dflt) sev = va_arg(va, int);
fmt = va_arg(va, const char *);
fprintf(stderr, "[%d] ", sev);
vfprintf(stderr, fmt, va);
fprintf(stderr, "\n");
va_end(va);
return 0;
}
int main()
{
PRINT(1, "Incompatible types %s and %s", "Apple", "Orange");
PRINT("Microphone test: %d, %d, %d, ...", 1, 2, 3);
return 0;
}
Это решение опасно, потому что функция msg
безопасна только в том случае, если она сгенерирована макросом. И макрос безопасен только в том случае, если строка формата является строковым литералом, начинающимся с двойной кавычки. Макрос расширяет аргументы на один логический аргумент влево и скрывает несовместимость аргументов в вариативном списке аргументов.
Это может быть хорошим трюком, но лучше иметь отдельные макросы с четкими именами.
person
M Oehm
schedule
15.09.2014