Создание макроса C с помощью ## и LINE (объединение токенов с макросом позиционирования)
Я хочу создать макрос C, который создает функцию с именем на основе по номеру линии. Я думал, что могу сделать что-то вроде (реальная функция будет иметь операторы в фигурных скобках):
#define UNIQUE static void Unique_##__LINE__(void) {}
который я надеялся расширить до чего-то вроде:
static void Unique_23(void) {}
Это не сработает. С конкатенацией маркеров, макросы позиционирования рассматриваются буквально, в конечном итоге расширяется до:
static void Unique___LINE__(void) {}
можно ли это сделать?
(Да, есть реальная причина, по которой я хочу сделать это, независимо от того, насколько бесполезно это кажется).
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.
GCC не требует "обертывания" (или реализации), если результат не должен быть "stringified". Gcc имеет функции, но все это можно сделать с простой версией C 1 (и некоторые утверждают, что Berkeley 4.3 C настолько быстрее, что стоит научиться использовать).
**Clang (llvm) не делает пробел правильно для расширения макроса - он добавляет пробелы (которые, безусловно, уничтожают результат как идентификатор C для дальнейшей предварительной обработки) **, clang просто не делает расширение # или * macro как C Препроцессор, как ожидается, в течение десятилетий. Основным примером является компиляция X11, макрос "Concat3" сломан, его результат теперь неверно назван идентификатором C, который, конечно же, не удается построить. и я начинаю что-то строить не получается - это их профессия.
Я думаю, что ответ здесь "новый C, который нарушает стандарты, плохой C", эти хаки всегда выбирают (clobber namespaces) они меняют значения по умолчанию без причины, но на самом деле не "улучшают C" (за исключением их собственного say so: which i say is contraption сделано, чтобы объяснить, почему они уходят со всеми поломками никто еще не сделал их ответственными за).
Это не проблема, что более ранние предварительные процессоры C не поддерживали UNIq_ ()__ потому что они поддерживали # pragma, которая позволяет "компилятору brand hackery в коде быть помеченным как hackery", а также функционировать так же хорошо, не влияя на стандарты: так же, как изменение значений по умолчанию бесполезно wonton breakage, и так же, как изменение того, что функция делает при использовании того же самого имя (пространство имен clobbering) есть ... вредоносные программы на мой взгляд