Создание макроса C с ## и __LINE__ (конкатенация токенов с макросом позиционирования)

107

Я хочу создать макрос C, который создает функцию с именем на основе номера строки. Я подумал, что могу сделать что-то вроде (у реальной функции в фигурных скобках есть операторы):

#define UNIQUE static void Unique_##__LINE__(void) {}

Я надеялся, что это расширится до чего-то вроде:

static void Unique_23(void) {}

Это не работает. При конкатенации токенов макросы позиционирования обрабатываются буквально, в конечном итоге расширяясь до:

static void Unique___LINE__(void) {}

Возможно ли это сделать?

(Да, есть реальная причина, по которой я хочу это сделать, независимо от того, насколько это бесполезно).

DD.
источник
Я думаю, вы можете заставить это работать с косвенным расширением макросов .
Бен Стиглиц,
4
возможный дубликат Как дважды объединить препроцессор C и развернуть макрос, как в "arg ## _ ## MACRO"? То же самое касается любого макроса, кроме __LINE__(хотя это обычный вариант использования.
Ciro Santilli 郝海东 冠状 病 六四 事件

Ответы:

176

Проблема в том, что когда у вас есть замена макроса, препроцессор будет рекурсивно разворачивать макросы только в том случае, если к нему не применяются ни строковый оператор, #ни оператор вставки токена ##. Итак, вам нужно использовать несколько дополнительных уровней косвенности, вы можете использовать оператор вставки токена с рекурсивно расширенным аргументом:

#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.

Адам Розенфилд
источник
3
Боюсь, что это не работает с GNU cpp. TOKENPASTE использует LINE как буквальное значение. TOKENPASTE (Unique_, LINE ) расширяется до Unique___LINE__
DD.
3
@DD: Да, сейчас исправлено. Для этого нужны 2 уровня косвенного обращения, а не 1.
Адам Розенфилд
__COUNTER__Макрос не работает для меня в НКУ; хотя __LINE__тот действительно работал, как рекламировалось.
Тайлер
2
Немного дополнительной информации для тех, кто пробует COUNTER , согласно msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx, это макрос, специфичный для Microsoft.
Elva
3
Любое объяснение, почему вам нужен 2 уровня косвенного обращения? Я пробовал это с одним, без # и ##, и это не расширяет его на VS2017. По-видимому, то же самое и с GCC. Но если вы добавите второй уровень косвенности, он расширится. Магия?
Гейб Халсмер
-3

GCC не требует «обертывания» (или реализации), если результат не нужно «преобразовать в строку». Gcc имеет функции, но ВСЕ может быть выполнено с помощью простого C версии 1 (и некоторые утверждают, что Berkeley 4.3 C настолько быстрее, что стоит научиться использовать).

** Clang (llvm) НЕ ИСПОЛЬЗУЕТ БЕЛЫЙ ПРОБЕЛ ПРАВИЛЬНО для раскрытия макроса - он добавляет пробел (который, безусловно, уничтожает результат как идентификатор C для дальнейшей предварительной обработки) **, clang просто не выполняет # или * расширение макроса в качестве препроцессора C ожидается на протяжении десятилетий. Ярким примером является компиляция X11, макрос «Concat3» не работает, результатом является MISNAMED C Identifier, который, конечно, не может быть собран. и я начинаю понимать, что неудачи в сборке - их профессия.

Я думаю, что ответ здесь - «новый C, который нарушает стандарты, - это плохой C», эти хаки всегда предпочитают (стирать пространства имен), они меняют значения по умолчанию без причины, но на самом деле не «улучшают C» (за исключением их собственного, так сказать: который я скажем, это хитрое изобретение, чтобы объяснить, почему им сходит с рук все поломки, за которые никто еще не возложил на них ответственность).


Не проблема, что более ранние препроцессоры C не поддерживали UNIq_ () __, потому что они поддерживали #pragma, которая позволяет «пометить хакерство в коде компилятора как хакерство», а также работать так же БЕЗ влияния на стандарты: так же, как изменение defaults - это бесполезная поломка wonton, и точно так же, как изменение того, что функция делает при использовании того же имени (затирание пространства имен), на мой взгляд ... вредоносное ПО

никто не
источник