Próbuję napisać program, w którym nazwy niektórych funkcji są zależne od wartości określonej zmiennej makra z makrem takim jak to:
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
int NAME(some_function)(int a);
Niestety, makro NAME()
zamienia to na
int some_function_VARIABLE(int a);
zamiast
int some_function_3(int a);
więc jest to zdecydowanie niewłaściwy sposób postępowania. Na szczęście liczba różnych możliwych wartości zmiennej VARIABLE jest niewielka, więc mogę po prostu zrobić #if VARIABLE == n
i wymienić wszystkie przypadki osobno, ale zastanawiałem się, czy istnieje sprytny sposób, aby to zrobić.
#define A 0 \n #define M a ## A
: posiadanie dwóch##
nie jest kluczem.Odpowiedzi:
Standardowy preprocesor C.
Dwa poziomy pośrednictwa
W komentarzu do innej odpowiedzi Cade Roux zapytał, dlaczego potrzebne są dwa poziomy pośrednictwa. Lekceważąca odpowiedź jest taka, ponieważ tego wymaga norma, aby działała; zwykle okazuje się, że potrzebujesz podobnej sztuczki z operatorem naciągania.
Sekcja 6.10.3 standardu C99 dotyczy „zastępowania makr”, a 6.10.3.1 - „zastępowania argumentów”.
W inwokacji
NAME(mine)
argumentem jest „mój”; jest w pełni rozszerzony na „mój”; jest następnie podstawiany w łańcuch zastępujący:Teraz makro EVALUATOR jest odkrywane, a argumenty są izolowane jako „moje” i „ZMIENNE”; ta ostatnia jest następnie w pełni rozwijana do „3” i zastępowana w zastępczym ciągu:
Działanie tego jest objęte innymi zasadami (6.10.3.3 „Operator ##”):
Tak, lista zawiera wymiana
x
następuje##
, a także##
następujey
; więc mamy:a eliminacja
##
tokenów i łączenie tokenów po obu stronach łączy „moje” z „_” i „3”, aby uzyskać:To jest pożądany rezultat.
Jeśli spojrzymy na oryginalne pytanie, kod był (dostosowany do używania „mine” zamiast „some_function”):
Argument do NAZWY jest wyraźnie „mój” i jest w pełni rozwinięty.
Zgodnie z zasadami 6.10.3.3 znajdujemy:
która po
##
wyeliminowaniu operatorów odwzorowuje:dokładnie tak, jak podano w pytaniu.
Tradycyjny preprocesor C.
Robert Rüger pyta :
Może, a może nie - zależy to od preprocesora. Jedną z zalet standardowego preprocesora jest to, że ma tę funkcję, która działa niezawodnie, podczas gdy istniały różne implementacje preprocesorów przed standardem. Jednym z wymagań jest to, że gdy preprocesor zastępuje komentarz, nie generuje spacji, jak jest to wymagane przez preprocesor ANSI. Preprocesor C GCC (6.3.0) spełnia to wymaganie; preprocesor Clang z XCode 8.2.1 tego nie robi.
Kiedy to działa, wykonuje to zadanie (
x-paste.c
):Zwróć uwagę, że nie ma spacji między
fun,
aVARIABLE
- to ważne, ponieważ jeśli jest obecny, jest kopiowany na wyjście, a kończy sięmine_ 3
na nazwie, która oczywiście nie jest poprawna składniowo. (Teraz, proszę, czy mogę odzyskać włosy?)Z GCC 6.3.0 (uruchomionym
cpp -traditional x-paste.c
) otrzymuję:Dzięki Clang z XCode 8.2.1 otrzymuję:
Te przestrzenie psują wszystko. Zauważam, że oba preprocesory są poprawne; różne preprocesory przed standardem wykazywały oba zachowania, co powodowało, że wklejanie tokenów było niezwykle denerwującym i zawodnym procesem podczas próby przeniesienia kodu. Norma z
##
notacją radykalnie to upraszcza.Mogą istnieć inne sposoby, aby to zrobić. Jednak to nie działa:
GCC generuje:
Blisko, ale bez kostek. YMMV oczywiście w zależności od preprocesora preprocesora, którego używasz. Szczerze mówiąc, jeśli utkniesz z preprocesorem, który nie współpracuje, prawdopodobnie prościej byłoby zaaranżować użycie standardowego preprocesora C zamiast preprocesora standardowego (zwykle jest sposób na odpowiednie skonfigurowanie kompilatora) niż spędzać dużo czasu próbując znaleźć sposób na wykonanie tej pracy.
źródło
cpp -traditional
. Zauważ, że nie ma ostatecznej odpowiedzi - zależy to od preprocesora, który masz.Szczerze mówiąc, nie chcesz wiedzieć, dlaczego to działa. Jeśli wiesz, dlaczego to działa, staniesz się w pracy tym facetem, który zna się na takich rzeczach, i każdy przyjdzie zadać ci pytania. =)
Edycja: jeśli naprawdę chcesz wiedzieć, dlaczego to działa, z radością opublikuję wyjaśnienie, zakładając, że nikt mnie nie przebije.
źródło