Создание макроса C С## и __LINE__ (конкатенация маркера с помощью макроса позиционирования)
Я хочу создать макрос C, который создает функцию с именем на основе
на номер строки.
Я думал, что смогу сделать что-то вроде (у реальной функции будут выражения в фигурных скобках):
#define UNIQUE static void Unique_##__LINE__(void) {}
Я надеялся, что это расширится до чего-то вроде:
static void Unique_23(void) {}
Это не работает. При объединении маркеров макросы позиционирования
обрабатываются буквально, заканчиваясь расширением до:
static void Unique___LINE__(void) {}
Можно ли это сделать?
(Да, есть реальная причина, по которой я хочу это сделать, независимо от того, насколько это бесполезно).
Ответы
Ответ 1
Проблема заключается в том, что при замене макросов препроцессор будет рекурсивно расширять макросы, если к нему не применяются ни оператор стриминга #
, ни оператор ##
. Таким образом, вы должны использовать некоторые дополнительные слои косвенности, вы можете использовать оператор-вставку с рекурсивно расширенным аргументом:
#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define UNIQUE static void TOKENPASTE2(Unique_, __LINE__)(void) {}
Затем __LINE__
расширяется до номера строки при расширении UNIQUE
(так как это не связано ни с #
, либо ##
), а затем при вставке маркера происходит расширение TOKENPASTE
.
Следует также отметить, что существует также макрос __COUNTER__
, который при каждом его вычислении расширяется до нового целого числа, если вам нужно иметь несколько экземпляров макроса UNIQUE
в той же строке. Примечание: __COUNTER__
поддерживается MS Visual Studio, GCC (начиная с V4.3) и Clang, но не является стандартным C.
Ответ 2
GCC не требует "обертывания" (или реализации), если результат не должен быть "стеновым". У Gcc есть функции, но ВСЕ могут быть выполнены с простой версией C версии 1 (и некоторые утверждают, что Berkeley 4.3 C намного быстрее, чем стоит изучать, как использовать).
** Clang (llvm) НЕ ИСПОЛЬЗУЕТ БЕЛЫЙ ПРОСТРАНСТВЕННО для расширения макроса - он добавляет пробелы (которые, безусловно, уничтожают результат как идентификатор C для дальнейшей предварительной обработки) **, clang просто не делает # или * Макрорасширение как препроцессор C ожидается на протяжении десятилетий. Первым примером является компиляция X11, макрос "Concat3" сломан, в результате получается MISNAMED C Identifier, который, конечно же, не может быть создан. и я начинаю с того, что строят сборки - это их профессия.
Я думаю, что ответ здесь "новый C, который нарушает стандарты, это плохо C", эти хаки всегда выбирают (clobber namespaces), они меняют значения по умолчанию без каких-либо причин, но на самом деле не улучшают C (за исключением их собственных утверждений: я говорю, что это приспособление, чтобы объяснить, почему они уходят со всех поломки, за которые никто не заставлял их отвечать).
Не проблема, что более ранние предварительные процессоры C не поддерживали UNIq _() __, потому что они поддерживали #pragma, которая позволяет "взломам брендов компилятора в коде быть помечены как хакерство", а также функция так же как БЕСПЛАТНЫЕ стандарты: так же, как изменение дефолтов - бесполезное поломка wonton, и так же, как изменение того, что делает функция при использовании того же имени (сплетение пространства имен), - это... вредоносное ПО, на мой взгляд