Chcę utworzyć makro C, które tworzy funkcję o nazwie na podstawie numeru wiersza. Pomyślałem, że mógłbym zrobić coś takiego (prawdziwa funkcja miałaby instrukcje w nawiasach):
#define UNIQUE static void Unique_##__LINE__(void) {}
Miałem nadzieję, że rozwinie się do czegoś takiego:
static void Unique_23(void) {}
To nie działa. W przypadku konkatenacji tokenów makra pozycjonowania są traktowane dosłownie, co kończy się rozszerzeniem do:
static void Unique___LINE__(void) {}
Czy to się da zrobić?
(Tak, istnieje prawdziwy powód, dla którego chcę to zrobić, bez względu na to, jak bezużyteczne wydaje się to).
c
macros
concatenation
token
DD.
źródło
źródło
__LINE__
(chociaż jest to powszechny przypadek użycia.Odpowiedzi:
Problem polega na tym, że gdy masz zamianę makra, preprocesor będzie rozszerzał makra rekurencyjnie tylko wtedy, gdy nie zostanie do niego zastosowany
#
ani operator stringizing, ani operator wklejania tokenu##
. Musisz więc użyć dodatkowych warstw pośrednich, możesz użyć operatora wklejania tokenu z rekursywnie rozszerzonym argumentem:Następnie
__LINE__
jest rozwijany do numeru linii podczas rozwijaniaUNIQUE
(ponieważ nie jest związany z ani#
lub##
), a następnie wklejanie tokenów odbywa się podczas rozwijaniaTOKENPASTE
.Należy również zauważyć, że istnieje również
__COUNTER__
makro, które rozwija się do nowej liczby całkowitej za każdym razem, gdy jest oceniane, na wypadek, gdyby trzeba było mieć wiele wystąpieńUNIQUE
makra w tym samym wierszu. Uwaga:__COUNTER__
jest obsługiwany przez MS Visual Studio, GCC (od wersji 4.3) i Clang, ale nie jest standardem C.źródło
__COUNTER__
Makro nie działa na mnie w gcc; chociaż ten__LINE__
działał zgodnie z reklamą.GCC nie wymaga „opakowywania” (ani realizacji), chyba że wynik wymaga „ujednolicenia”. Gcc ma funkcje, ale WSZYSTKIE można zrobić za pomocą zwykłego C w wersji 1 (a niektórzy twierdzą, że Berkeley 4.3 C jest o wiele szybszy, że warto nauczyć się go używać).
** Clang (llvm) NIE WYKONUJE PRAWIDŁOWO WHITE SPACE dla rozwijania makr - dodaje białe znaki (co z pewnością niszczy wynik jako identyfikator C do dalszego przetwarzania wstępnego) **, clang po prostu nie wykonuje rozszerzenia # lub * makro jak oczekuje się od preprocesora C przez dziesięciolecia. Podstawowym przykładem jest kompilacja X11, makro "Concat3" jest zepsute, jego wynikiem jest teraz MISNAMED C Identifier, którego oczywiście nie można zbudować. i zaczynam myśleć, że zawodzenie budowy to ich zawód.
Myślę, że tutaj odpowiedź brzmi "nowe C, które łamie standardy, jest złe C", ci hakerzy zawsze wybierają (przebijają przestrzenie nazw), zmieniają domyślne bez powodu, ale tak naprawdę nie "ulepszają C" (z wyjątkiem własnego powiedzmy, że to urządzenie zostało stworzone, aby wyjaśnić, dlaczego uszło im to na sucho z wszystkimi uszkodzeniami, za które nikt jeszcze nie uczynił ich odpowiedzialnymi).
Nie jest problemem, że wcześniejsze preprocesory C nie obsługiwały UNIq_ () __, ponieważ obsługiwały #pragma, która umożliwia oznaczenie „hakera marki kompilatora w kodzie jako włamanie”, a także działa równie dobrze BEZ wpływu na standardy: tak samo jak zmiana defaults to bezużyteczne wonton breakage, i tak samo jak zmiana tego, co robi funkcja przy użyciu tej samej nazwy (clobbering przestrzeni nazw) jest ... moim zdaniem złośliwym oprogramowaniem
źródło