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

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

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

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

static void Unique_23(void) {}

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

static void Unique___LINE__(void) {}

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


person DD.    schedule 20.10.2009    source источник
comment
Я думаю, вы можете заставить это работать с помощью косвенного расширения макроса.   -  person Ben Stiglitz    schedule 21.10.2009
comment
возможный дубликат Как дважды выполнить конкатенацию с препроцессором C и развернуть макрос, как в arg ## _ ## MACRO? То же самое касается любого макроса, кроме __LINE__ (хотя это общий вариант использования.   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 21.06.2015


Ответы (2)


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

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

person Adam Rosenfield    schedule 20.10.2009
comment
Боюсь, что это не работает с GNU cpp. TOKENPASTE использует LINE как буквальное значение. TOKENPASTE (Unique_, LINE) заменяется на Unique___LINE__ - person DD.; 21.10.2009
comment
@DD: Да, сейчас исправлено. Ему нужно 2 уровня косвенного обращения, а не 1. - person Adam Rosenfield; 21.10.2009
comment
Макрос __COUNTER__ у меня не работал в gcc; хотя __LINE__ действительно работал, как рекламировалось. - person Tyler; 30.09.2011
comment
Немного дополнительной информации для тех, кто пытается COUNTER, согласно msdn.microsoft.com/en-us/library/b0084kay (v = vs.80) .aspx это макрос, специфичный для Microsoft. - person Elva; 04.03.2012
comment
@Yourdoom: GCC также поддерживает __COUNTER__ (начиная с V4.3). Я отредактировал ответ. - person sleske; 05.06.2014
comment
Любое объяснение, почему вам нужен 2 уровня косвенного обращения? Я пробовал это с одним, без # и ##, и это не расширяет его на VS2017. По-видимому, то же самое и с GCC. Но если вы добавите второй уровень косвенности, он расширится. Магия? - person Gabe Halsmer; 16.11.2017
comment
@AdamRosenfield, можете ли вы посмотреть на аналогичный вопрос stackoverflow.com/questions / 65798613 / - person gaurav bharadwaj; 19.01.2021

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

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

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


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

person nobodyisaliased    schedule 15.03.2017