2015-08-25 8 views
7

Właśnie się dowiedziałem, że gcc wydaje się traktować wynik rozszerzenia makra podobnego do funkcji jako oddzielny token. Oto prosty przykład pokazujący zachowanie gcc:Rozbudowa makra podobnego do funkcji tworzy oddzielny token

#define f() foo 
void f()_bar(void); 
void f()bar(void); 
void f()-bar(void); 

Kiedy wykonać gcc -E -P test.c (działa tylko preprocesor), pojawia się następujący komunikat:

void foo _bar(void); 
void foo bar(void); 
void foo-bar(void); 

Wydaje się, że w pierwszych dwóch definicje, gcc wstawia spację po rozwiniętym makrze, aby upewnić się, że jest to oddzielny token. Czy to naprawdę się dzieje?

Czy jest to wymagane przez dowolny standard (nie mogłem znaleźć dokumentacji na ten temat)?

Chcę ustawić _bar część tego samego tokena. Czy jest jakiś sposób to zrobić? Mógłbym użyć tokenu operatora konkatenacji ##, ale będzie on wymagał kilku poziomów makr (ponieważ w prawdziwym kodzie f() jest bardziej złożony). Zastanawiam się, czy istnieje proste (i prawdopodobnie bardziej czytelne) rozwiązanie.

+1

Czy Google jest wyłączony? Czy nawet nie próbowałeś znaleźć C [standardu] (http://port70.net/~nsz/c/c11/n1570.html#6.10.3), który jest ** pozornie ** autorytatywnym źródłem do konsultacji. – Olaf

+1

@Olaf - Nie znalazłem żadnego wyraźnego oświadczenia odpowiadającego na moje pytanie ani w C99, ani na linku, który wskazałeś mi. – martinkunev

+4

Aby być uczciwym zachowanie opisane w standardzie nie jest całkowicie oczywiste. Domyślam się, że słowo kluczowe to, że preprocesor zamienia makro na * tokeny *, co oznacza wstawienie spacji w razie potrzeby. – nemetroid

Odpowiedz

1

wydaje się, że w pierwszych dwóch definicji, GCC wkładki przestrzeń po rozszerzonym makro, aby zapewnić, że jest to odrębny znak. Czy to naprawdę się dzieje?

Tak.

Czy jest to wymagane przez dowolny standard (nie mogłem znaleźć dokumentacji na ten temat)?

Tak, chociaż implementacja może wstawić nawet więcej niż jeden spację do oddzielenia tokenów.

f()_bar

tutaj masz 4 znaki po analizie leksykalnej (są one rzeczywiście tokeny pre-processor na tym etapie, ale nazwijmy je żetony): f, (, ) i _bar.

Funkcja jak makro wymiana semantycznej (jak zdefiniowano w C11, 6.10.3) ma zastąpić 3 znacznik f, (, ) się na nową foo. Nie można pracować na innych tokenach i zmieniać ostatniego tokenu _bar. W tym celu implementacja musi wstawić co najmniej jeden spację do zachowania tokena _bar. W przeciwnym razie wynik byłby foo_bar, który jest pojedynczym tokenem.

gcc preprocesor nieco documents to tutaj:

Gdy plik wejściowy jest dzielony na tokeny, granice symboliczne nigdy nie zmieni, chyba że operator „##” przerób służy do wklejenia tokeny razem. Zobacz konkatenację. Na przykład,

#define foo() bar 
foo()baz 
    ==> bar baz 
not 
    ==> barbaz 

W innym przypadku, jak f()-bar nie 5 żetonów: f, (, ), - i bar. (- to token interpunkcyjny w C, natomiast _ w _bar jest po prostu znakiem tokena identyfikatora). Implementacja nie musi wstawiać separatora tokenów (jako białych znaków) tutaj, ponieważ po zamianie makr -bar nadal traktowane są jako dwa osobne tokeny ze składni C.

gcc preprocesor (cpp) nie wstawia białych znaków tylko dlatego, że nie musi. W cppdocumentation na symboliczną odstępów jest napisane (na innym numerze):

Jednak chcielibyśmy zachować przestrzeń wkładanie do minimum, zarówno ze względów estetycznych, a ponieważ powoduje problemy dla ludzi, którzy wciąż próbują nadużywać preprocesora dla takich źródeł jak Fortran i Makefile.

ja nie odniósł się do rozwiązania problemu w tej odpowiedzi, ale myślę, że trzeba użyć operatora wyraźnie określony, aby złączyć tokeny: token operatora tapetowania ##.

1

Jedynym sposobem mogę myśleć (jeśli nie można korzystać z tokena operatorem konkatenacji ##) jest metodą tradycyjną (pre-standard) C preprocessing:

gcc -E -P -traditional-cpp test.c 

wyjściowa:

void foo_bar(void); 
void foobar(void); 
void foo-bar(void); 

More info