Проблема, с которой я столкнулся при использовании приведенного ниже макроса
// Assume that __GNUC__ or __clang__ is defined.
#if defined(__has_c_attribute)
#if __has_c_attribute(deprecated)
#define ATTRIBUTE_DEPRECATED(...) [[deprecated(__VA_ARGS__)]]
#endif /* deprecated */
#else
#define ATTRIBUTE_DEPRECATED(...) __attribute__((deprecated(__VA_ARGS__)))
#endif /* __has_c_attribute */
в том, что __attribute__((deprecated()))
и __attribute__((deprecated))
эквивалентны, а [[deprecated()]]
и [[deprecated]]
нет.
Блок #else
работает корректно при вызове с помощью любого из них:
ATTRIB_DEPRECATED()
ATTRIB_DEPRECATED("privat")
потому что GNU C допускает, чтобы список параметров атрибута был пустым.
Но при компиляции с std=c2x
блок #if
этого не делает и происходит сбой:
deprecated.c:12:14: error: parentheses must be omitted if attribute argument list is empty
Есть ли способ заставить тот же код работать и с C23?
Еще было бы лучше, если бы я мог написать:
ATTRIB_DEPRECATED
вместо:
ATTRIB_DEPRECATED()
если бы не было аргументов.
Для MRE:
// Assume that __GNUC__ or __clang__ is defined.
#if defined(__has_c_attribute)
#if __has_c_attribute(deprecated)
#define ATTRIBUTE_DEPRECATED(...) [[deprecated(__VA_ARGS__)]]
#endif
#else
#define ATTRIBUTE_DEPRECATED(...) __attribute__((deprecated(__VA_ARGS__)))
#endif /* __has_c_attribute */
ATTRIBUTE_DEPRECATED() int min(int a, int b)
{
return a < b ? a : b;
}
int main() {
return min(10, 20);
}
Приведенный выше код компилировал бы только f __has_c_attribute
не был определен.
Я считаю, что для этого вам нужно иметь два отдельных макроса
#if defined(__has_c_attribute)
#if __has_c_attribute(deprecated)
#define ATTRIBUTE_DEPRECATED [[deprecated]]
#define ATTRIBUTE_DEPRECATEDR(...) [[deprecated(#__VA_ARGS__)]]
#endif
#else
#define ATTRIBUTE_DEPRECATED __attribute__((deprecated))
#define ATTRIBUTE_DEPRECATEDR(...) __attribute__((deprecated))
#endif /* __has_c_attribute */
ATTRIBUTE_DEPRECATEDR(this is test) int min(int a, int b)
{
return a < b ? a : b;
}
ATTRIBUTE_DEPRECATED int max(int a, int b)
{
return a > b ? a : b;
}
int main() {
return min(max(10,20), max(10, 20));
}
Просто ВА_ОПТ это.
#define ATTRIBUTE_DEPRECATED(...) [[deprecated __VA_OPT__( (__VA_ARGS__) )]]
ATTRIBUTE_DEPRECATED()
ATTRIBUTE_DEPRECATED("bla")
Ниже без VA_OPT. Сначала берем код из https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ . Затем мы добавляем некоторый помощник по перегрузке, основанный на возврате ISEMPTY()
. Каждая перегрузка просто добавляет аргумент или нет.
#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define _TRIGGER_PARENTHESIS_(...) ,
#define ISEMPTY(...) \
_ISEMPTY( \
/* test if there is just one argument, eventually an empty \
one */ \
HAS_COMMA(__VA_ARGS__), \
/* test if _TRIGGER_PARENTHESIS_ together with the argument \
adds a comma */ \
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
/* test if the argument together with a parenthesis \
adds a comma */ \
HAS_COMMA(__VA_ARGS__ (/*empty*/)), \
/* test if placing it between _TRIGGER_PARENTHESIS_ and the \
parenthesis adds a comma */ \
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)) \
)
#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _ISEMPTY(_0, _1, _2, _3) HAS_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define _IS_EMPTY_CASE_0001 ,
// -----------------------------
#define CONCAT(a, b) a##b
#define XCONCAT(a, b) CONCAT(a, b)
#define ATTRIBUTE_DEPRECATED1() \
[[deprecated]]
#define ATTRIBUTE_DEPRECATED0(...) \
[[deprecated(__VA_ARGS__)]]
#define ATTRIBUTE_DEPRECATED(...) \
XCONCAT(ATTRIBUTE_DEPRECATED, ISEMPTY(__VA_ARGS__))(__VA_ARGS__)
ATTRIBUTE_DEPRECATED()
ATTRIBUTE_DEPRECATED("bla")
Обратите внимание: передача пустого списка ()
для макроса с помощью (...)
технически не соответствует требованиям и нарушает https://port70.net/~nsz/c/c11/n1570.html#6.10.3p4 . Но никого это не волнует.