Tworzenie makra C z ## i __LINE__ (konkatenacja tokenów z makrem pozycjonującym)


107

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


Myślę, że można to zmusić do pracy z pośrednią ekspansją makr .
Ben Stiglitz

4
możliwy duplikat Jak dwukrotnie połączyć z preprocesorem C i rozwinąć makro jak w „arg ## _ ## MACRO”? To samo dotyczy każdego makra oprócz __LINE__(chociaż jest to powszechny przypadek użycia.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:


176

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:

#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define UNIQUE static void TOKENPASTE2(Unique_, __LINE__)(void) {}

Następnie __LINE__jest rozwijany do numeru linii podczas rozwijania UNIQUE(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ń UNIQUEmakra 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.


3
Obawiam się, że to nie działa z GNU cpp. TOKENPASTE używa LINE jako dosłownego. TOKENPASTE (Unique_, LINE ) rozwija się do Unique___LINE__
DD.

3
@DD: D'oh, naprawione teraz. Potrzebuje 2 warstw pośrednich, a nie 1.
Adam Rosenfield

__COUNTER__Makro nie działa na mnie w gcc; chociaż ten __LINE__działał zgodnie z reklamą.
Tyler,

2
Trochę dodatkowych informacji dla każdego, kto próbuje COUNTER , według msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx jest to makro specyficzne dla firmy Microsoft.
Elva

3
Jakieś wyjaśnienie, dlaczego potrzebujesz 2 poziomów pośrednich? Wypróbowałem to tylko z jednym, brakiem # i ##, i to nie rozszerza go na VS2017. Najwyraźniej to samo dotyczy GCC. Ale jeśli dodasz drugi poziom pośredni, to się rozszerzy. Magia?
Gabe Halsmer

-2

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.