Dlaczego potrzebuję podwójnej warstwy indrection dla makr?

At: C++ FAQ-różne problemy techniczne - [39.6] co należy zrobić z makrami, które muszą wkleić dwa tokeny razem?

Czy ktoś mógłby mi wyjaśnić dlaczego ? Wszystko co czytam to zaufaj mi , ale po prostu nie mogę ufać w coś, bo ktoś tak powiedział.

Próbowałem podejścia i nie mogę znaleźć żadnych błędów pojawiających się:

#define mymacro(a) int a ## __LINE__
mymacro(prefix) = 5;
mymacro(__LINE__) = 5;
int test = prefix__LINE__*__LINE____LINE__; // fine

Więc dlaczego Czy muszę to robić w ten sposób (cytat ze strony):

Jednak potrzebujesz podwójnej warstwy indrection, gdy używasz ##. Zasadniczo musisz utworzyć specjalne makro do "wklejania tokenów" takie as:

 #define NAME2(a,b)         NAME2_HIDDEN(a,b)
 #define NAME2_HIDDEN(a,b)  a ## b 

Zaufaj mi - naprawdę musisz to zrobić to! (I proszę niech nikt mi nie pisze, że czasami działa bez druga warstwa indrection. Spróbuj połączyć symbol z __ LINE _ _ i zobaczyć, co się wtedy stanie.)

Edit: Czy ktoś mógłby też wyjaśnić dlaczego używa NAME2_HIDDEN zanim zostanie to zadeklarowane poniżej? Informatyka bardziej logiczne wydaje się zdefiniowanie makra NAME2_HIDDEN zanim go użyję. To jakaś sztuczka?

Author: TWOF, 2011-11-22

5 answers

Odpowiednia część C spec:

6.10.3.1 podstawienie argumentu

Po zidentyfikowaniu argumentów wywołania makra podobnego do funkcji, następuje podstawienie argumentu. Parametr na liście zastępczej, chyba że poprzedzony przez # lub # # Token wstępnego przetwarzania lub po nim # # Token wstępnego przetwarzania (patrz poniżej), jest zastąpiony przez odpowiedni argument po tym, jak wszystkie makra w nim zawarte zostały Rozszerzony. Przed byciem podstawione, tokeny wstępnego przetwarzania każdego argumentu są całkowicie makro zastąpione tak, jakby tworzyły resztę pliku wstępnego przetwarzania; żadne inne dostępne są tokeny wstępnego przetwarzania.

Kluczową częścią, która określa, czy chcesz mieć podwójną indrection, czy nie, jest drugie zdanie i wyjątek w nim -- jeśli parametr jest zaangażowany w operację # lub ## (np. params w mymacro i NAME2_HIDDEN), to żadne inne makra w argumencie nie są rozwijane przed robi # LUB ##. Jeśli natomiast w ciele makra nie ma # lub ## bezpośrednio (jak w przypadku NAME2), to inne makra w parametrach są rozszerzane.

Więc sprowadza się do tego, co chcesz-czasami chcesz, aby wszystkie makra zostały najpierw rozbudowane, a następnie wykonać # LUB ## (w tym przypadku chcesz dwuwarstwową indirection), a czasami nie chcesz, aby makra zostały najpierw rozbudowane (w tym przypadku nie możesz mieć dwuwarstwowych makr, musisz to zrobić bezpośrednio.)

 30
Author: Chris Dodd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-28 17:56:39

__LINE__ jest specjalnym makrem, które ma rozwiązać do bieżącego numeru linii. Jeśli jednak wykonasz wklejenie tokenu za pomocą __LINE__ bezpośrednio, nie ma to szansy na rozwiązanie, więc kończysz z tokenem prefix__LINE__ zamiast, powiedzmy, prefix23, Jak prawdopodobnie spodziewałbyś się, gdybyś napisał ten kod w dziczy.

 4
Author: John Calsbeek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-22 18:43:14

Chris Dodd ma doskonałe wyjaśnienie pierwszej części twojego pytania. Jeśli chodzi o drugą część, dotyczącą sekwencji definicji, skrócona wersja jest taka, że dyrektywy #define same w sobie nie są w ogóle oceniane; są one oceniane i rozszerzane tylko wtedy, gdy symbol znajduje się w innym miejscu Pliku. Na przykład:

#define A a  //adds A->a to the symbol table
#define B b  //adds B->b to the symbol table

int A;

#undef A     //removes A->a from the symbol table
#define A B  //adds A->B to the symbol table

int A;

Pierwszy int A; staje się int a;, ponieważ tak definiuje się A w tym punkcie pliku. Drugi int A; staje się int b; po dwóch rozszerzeniach. On najpierw rozszerzony do int B;, ponieważ A jest zdefiniowany jako B w tym momencie pliku. Następnie preprocesor rozpoznaje, że B jest makrem, gdy sprawdza tabelę symboli. {[9] } jest następnie rozszerzony do b.

Jedyną rzeczą, która się liczy, jest definicja symbolu w punkcie ekspansji, niezależnie od tego, gdzie jest definicja.

 3
Author: IronMensan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-22 20:07:37

Kolejność deklarowania makr nie jest ważna, kolejność ich użycia jest. Gdybyś faktycznie użył tego makra zanim zostało ono zadeklarowane -- (w rzeczywistym kodzie, czyli nie w makrze, które pozostaje uśpione do momentu wywołania) wtedy dostałbyś błąd, ale ponieważ większość zdrowych ludzi nie robi tego rodzaju rzeczy, pisząc makro, a następnie pisząc funkcję, która używa makra jeszcze nie zdefiniowanego w dół, itd., itp... Wygląda na to, że twoje pytanie nie jest tylko jednym pytanie, ale odpowiem tylko na jedną część. Myślę, że powinieneś to jeszcze bardziej rozłożyć.

 1
Author: osirisgothra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-15 09:11:10

Najbardziej nietechniczna odpowiedź, którą zebrałem ze wszystkich linków tutaj, i link linków;) jest taka, że jednowarstwowa indrection macro(x) #x stringifies nazwa wprowadzonego makra, ale używając podwójnych warstw, będzie to stringify wartość wprowadzonego makra.

#define valueOfPi 3
#define macroHlp(x) #x
#define macro(x) macroHlp(x)  
#define myVarOneLayer "Apprx. value of pi = " macroHlp(valueOfPi)
#define myVarTwoLayers "Apprx. value of pi = " macro(valueOfPi)

printf(myVarOneLayer); // out: Apprx. value of pi = valueOfPi 
printf(myVarOTwoLayers); // out: Apprx. value of pi = 3

Co się dzieje w printf(myVarOneLayer)

printf(myVarOneLayer) jest rozszerzony do printf("Apprx. value of pi = " macroHlp(valueOfPi))

macroHlp(valueOfPi) próbuje przeciągnąć dane wejściowe, samo dane wejściowe nie są oceniane. Jedynym celem w życiu jest wzięcie udziału i naciągnięcie. Więc rozszerza się na "valueOfPi"

Więc, co się dzieje w printf(myVarTwoLayers)

printf(myVarTwoLayers) jest rozszerzony do printf("Apprx. value of pi = " macro(valueOfPi)

macro(valueOfPi) nie ma operacji stringification, tzn. nie ma #x w jego rozszerzeniu, ale jest x, więc musi ocenić x i wprowadzić wartość do macroHlp dla stringification. Rozszerza się do macroHlp(3), co z kolei będzie ciągiem liczby 3, ponieważ używa #x

 1
Author: niCk cAMel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-08 21:55:12