Создание макроса 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, и так же, как изменение того, что делает функция при использовании того же имени (сплетение пространства имен), - это... вредоносное ПО, на мой взгляд