Czy za pomocą preprocesora C / C ++ można policzyć wiersze w pliku źródłowym do makra lub innej wartości dostępnej w czasie kompilacji? Np. Czy mogę zamienić MAGIC1
, MAGIC2
i MAGIC3
poniżej, i jakoś uzyskać wartość 4 podczas używania MAGIC3
?
MAGIC1 // can be placed wherever you like before the relevant
// lines - either right before them, or in global scope etc.
foo(); MAGIC2
bar(); MAGIC2
baz(); MAGIC2
quux(); MAGIC2
// ... possibly a bunch of code here; not guaranteed to be in same scope ...
MAGIC3
Uwagi:
- Specyficzne dla kompilatora rozszerzenia możliwości preprocesora są dopuszczalne, ale niepożądane.
- Jeśli jest to możliwe tylko przy pomocy części C ++, w przeciwieństwie do C, konstruuj, jest to również dopuszczalne, ale niepożądane (tzn. Chciałbym coś, co działałoby dla C).
- Oczywiście można to zrobić, uruchamiając plik źródłowy za pomocą zewnętrznego skryptu procesora, ale nie o to pytam.
c++
c-preprocessor
einpoklum
źródło
źródło
__LINE__
które reprezentuje bieżący numer linii__COUNTER__
i / lubBOOST_PP_COUNTER
?int arr[MAGIC4]
i uzyskania liczby wierszy w pewnej wcześniej policzonej części mojego kodu.Odpowiedzi:
Istnieje
__LINE__
makro preprocesora, które podaje liczbę całkowitą, na której pojawi się linia. Możesz wziąć jego wartość w pewnym wierszu, a następnie w późniejszym wierszu i porównać.Jeśli chcesz policzyć wystąpienia czegoś, a nie linii źródłowych,
__COUNTER__
może być niestandardową opcją obsługiwaną przez niektóre kompilatory, takie jak GCC i MSVC.Wziąłem wartość początkową,
__COUNTER__
ponieważ mogła być wcześniej użyta w pliku źródłowym lub w dołączonym nagłówku.W C zamiast w C ++ istnieją ograniczenia zmiennych stałych, więc
enum
zamiast tego można zastosowaćZamiana stałej na
enum
:źródło
__COUNTER__
nie jest standardem w C lub C ++. Jeśli wiesz, że działa z określonymi kompilatorami, określ je.BEFORE
iAFTER
nie są makramiWiem, że żądanie OP dotyczy użycia makr, ale chciałbym dodać inny sposób, który nie wymaga użycia makr.
C ++ 20 wprowadza
source_location
klasę reprezentującą pewne informacje o kodzie źródłowym, takie jak nazwy plików, numery linii i nazwy funkcji. W tym przypadku możemy to łatwo wykorzystać.I przykład na żywo tutaj .
źródło
source_location
być eksperymentalnym w C ++ 20?source_location
jest teraz oficjalnie częścią C ++ 20. Sprawdź tutaj . Po prostu nie mogłem znaleźć wersji kompilatora gcc na godbolt.org, która już go obsługuje w sensie eksperymentalnym. Czy możesz wyjaśnić nieco więcej swoje oświadczenie - mogę używać tylko liczby wierszy w tym samym zakresie, co linie, które policzyłem ?line_number_start
iline_number_end
w tym zakresie, nigdzie indziej. Jeśli chcę go gdzie indziej, muszę przekazać go w czasie wykonywania - co nie pozwala na osiągnięcie celu.line_number_end
widoczne w czasie kompilacji poza jego zasięgiem. Popraw mnie, jeśli się mylę.Dla kompletności: jeśli chcesz dodać
MAGIC2
po każdej linii, możesz użyć__COUNTER__
:https://godbolt.org/z/i8fDLx (zwraca
3
)Możesz ustawić go jako wielokrotnego użytku, przechowując wartości początkowe i końcowe
__COUNTER__
.Ogólnie jest to jednak bardzo uciążliwe. Nie będziesz też mógł liczyć wierszy zawierających dyrektywy preprocesora lub zakończonych
//
komentarzami. Użyłbym__LINE__
zamiast tego, zobacz inną odpowiedź.źródło
static_assert
?__COUNTER__
początkowo jest to zero, ponieważ inne nagłówki itp. Mogą go użyć.__COUNTER__
dwa razy i wziąć różnicę__COUNTER__
sam w sobie nie byłby dozwolony i musi zostać rozwinięty do czegoś, inaczej nie będzie się liczył (nie pamiętam reguł w 100% na ten temat).Trochę bardziej niezawodne rozwiązanie, pozwalające na różne liczniki (o ile nie łączą się i nie można ich używać
__COUNTER__
do innych zadań):Ukrywa to szczegóły implementacji (chociaż ukrywa je w makrach ...). Jest to uogólnienie odpowiedzi @ MaxLanghof. Zauważ, że
__COUNTER__
może mieć niezerową wartość, gdy zaczynamy odliczanie.Oto jak jest używany:
Jest to również poprawne C - jeśli twój preprocesor obsługuje
__COUNTER__
, to znaczy.Działa na GodBolt .
Jeśli używasz C ++, można zmodyfikować to rozwiązanie nawet nie zanieczyszczać globalnej przestrzeni nazw - poprzez umieszczenie liczniki wewnątrz
namespace macro_based_line_counts { ... }
lubnamespace detail
itd.)źródło
Na podstawie komentarza, jeśli chcesz określić rozmiar tablicy (czas kompilacji) w C lub C ++, możesz to zrobić
Jeśli potrzebujesz
sizeof(array)
w interweniujących liniach, możesz zastąpić je odwołaniem do zmiennej statycznej (chyba że absolutnie musi to być wyrażenie całkowite stałe), a kompilator optymalizujący powinien traktować go tak samo (eliminując potrzebę umieszczania zmiennej statycznej w pamięci)Rozwiązanie
__COUNTER__
oparte na bazie (jeśli to rozszerzenie jest dostępne), w przeciwieństwie do rozszerzenia__LINE__
opartego na zasadach , będzie działać tak samo.constexpr
s w C ++ powinien działać równie dobrzeenum
, aleenum
będzie również działał w zwykłym C (moje powyższe rozwiązanie jest zwykłym C).źródło
__COUNTER__
oparte na problemach również ma problemy: lepiej mieć nadzieję, że twoje magiczne makro jest jedynym użytkownikiem__COUNTER__
, przynajmniej zanim skończysz używać__COUNTER__
. Problem w zasadzie sprowadza się do prostych faktów, które__COUNTER__/__LINE__
są funkcjami preprocesora, a preprocesor działa w jednym przebiegu, więc nie można później zastąpić wyrażenia stałego liczbą całkowitą na podstawie__COUNTER__
/__LINE__
. Jedynym sposobem (przynajmniej w C) jest uniknięcie takiej potrzeby, np. Poprzez zastosowanie deklaracji tablicy bez rozmiaru (niepełne deklaracje typu tablicy).\
nie wpływa__LINE__
- jeśli dojdzie do przerwania linii,__LINE__
wzrasta. Przykład 1 , przykład 2 .