Standardowe predefiniowane makro __FILE__dostępne w języku C pokazuje pełną ścieżkę do pliku. Czy jest jakiś sposób na skrócenie ścieżki? Mam na myśli zamiast
Byłoby naprawdę wspaniale znaleźć rozwiązanie tylko dla preprocesora. Obawiam się, że sugestie oparte na operacjach na łańcuchach zostaną wykonane w czasie wykonywania.
cdleonard
9
Ponieważ używasz gcc, myślę, że możesz zmienić to, co __FILE__zawiera, zmieniając nazwę pliku, którą podajesz w linii poleceń. Więc zamiast tego gcc /full/path/to/file.cspróbuj cd /full/path/to; gcc file.c; cd -;. Oczywiście jest to trochę więcej, jeśli polegasz na bieżącym katalogu gcc jako ścieżki dołączania lub lokalizacji pliku wyjściowego. Edycja: dokumentacja gcc sugeruje, że jest to pełna ścieżka, a nie argument nazwy pliku wejściowego, ale to nie jest to, co widzę dla gcc 4.5.3 na Cygwin. Więc równie dobrze możesz wypróbować go w systemie Linux i zobaczyć.
Steve Jessop,
4
GCC 4.5.1 (zbudowany specjalnie dla arm-none-eabi) używa dokładnego tekstu nazwy pliku w swoim wierszu poleceń. W moim przypadku była to wina IDE, która wywołała GCC ze wszystkimi nazwami plików w pełni kwalifikowanymi, zamiast umieszczania bieżącego katalogu w rozsądnym miejscu (być może lokalizacja pliku projektu?) Lub konfigurowalnym i używając ścieżek względnych z tego miejsca. Podejrzewam, że wiele IDE robi to (szczególnie w systemie Windows) z powodu pewnego rodzaju niewygody związanej z wyjaśnianiem, gdzie naprawdę znajduje się „bieżący” katalog aplikacji GUI.
RBerteig
3
@SteveJessop - mam nadzieję, że przeczytałeś ten komentarz. Mam sytuację, w której widzę __FILE__wydrukowany jako ../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.ci chcę wiedzieć, gdzie gcc skompilował plik w taki sposób, że ten plik jest względnie zlokalizowany, tak jak pokazano.
Chan Kim,
1
To pytanie nie jest naśladownictwem pytania połączonego. Po pierwsze, link dotyczy C ++, a odpowiedzi konsekwentnie zagłębiają się w ezoterykę makr C ++. Po drugie, w pytaniu OP nie ma niczego, co wymagałoby rozwiązania makro. Tylko uroczyście wskazuje na problem i zadaje otwarte pytanie.
/jest prawidłowym separatorem ścieżek w systemie Windows.
Hans Passant
11
/jest prawidłowym separatorem ścieżki w nazwach plików przekazywanych do funkcji CreateFile () i tak dalej. Jednak nie zawsze oznacza to, że można używać /dowolnego miejsca w systemie Windows, ponieważ istnieje tradycja (odziedziczona z CPM) używania /jako argumentu prowadzącego w wierszu polecenia. Ale narzędziem jakość byłby ostrożny w nazwach plików podziału na obu slash i znaki backslash, aby uniknąć wszelkich problemów dla ludzi, że uda się wykorzystać /.
RBerteig
5
@AmigableClarkKant, nie, możesz łączyć oba separatory w tej samej nazwie pliku.
RBerteig
2
Jeśli Twoja platforma to obsługuje, char* fileName = basename(__FILE__); na pewno jest dostępna w systemie Linux i OS X, ale nie wiem o systemie Windows.
JeremyP,
14
Można to skrócić do strrchr("/" __FILE__, '/') + 1. Poprzez poprzedzanie „/” na __FILE__, strrchr ma pewność, że coś znajdzie, a zatem warunek?: Nie jest już potrzebny.
Obawiam się, że to nie zadziała dla PLIKU, do którego odwołuje się plik nagłówkowy.
Baiyan Huang
2
Zgadzam się z @BaiyanHuang, ale nie jestem pewien, czy komentarz jest jasny. __FILE__nie jest prostym symbolem preprocesora, zmiany w bieżącym pliku są często używane do emisji nazwy bieżącego pliku (nagłówka lub modułu źródłowego). To __FILENAME__musiałby tylko źródło zewnętrzną
nhed
3
Rozwiązanie tej odpowiedzi nie jest przenośne, ponieważ wykorzystuje ucieczki powłoki Bourne'a. Lepiej użyć CMake, aby zaimplementować go w czysty i przenośny sposób. Zobacz odpowiedź na makro define_file_basename_for_sources tutaj.
Colin D Bennett
3
Wariant GNU Make to CFLAGS + = -D__ NAZWA PLIKU __ = \ "$ (notdir $ <) \"
ctuffli
4
@firegurafiku Na platformach wbudowanych rozmiar kodu jest często bardziej ograniczeniem niż szybkością. Jeśli w każdym pliku znajdują się instrukcje debugowania, mogą one szybko zwiększyć rozmiar pliku za pomocą ciągów pełnych ścieżek. W tej chwili mam do czynienia z taką platformą, a odwoływanie się __FILE__wszędzie wypada z przestrzeni kodu.
mrtumnus
26
Właśnie wymyśliłem świetne rozwiązanie, które działa zarówno z plikami źródłowymi, jak i nagłówkowymi, jest bardzo wydajne i działa w czasie kompilacji na wszystkich platformach bez rozszerzeń specyficznych dla kompilatora. To rozwiązanie zachowuje również względną strukturę katalogów projektu, dzięki czemu wiesz, w którym folderze znajduje się plik, i tylko w odniesieniu do katalogu głównego projektu.
Chodzi o to, aby uzyskać rozmiar katalogu źródłowego za pomocą narzędzia do budowania i po prostu dodać go do __FILE__makra, całkowicie usuwając katalog i wyświetlając tylko nazwę pliku, zaczynając od katalogu źródłowego.
Poniższy przykład jest zaimplementowany przy użyciu CMake, ale nie ma powodu, dla którego nie działałby z innymi narzędziami do kompilacji, ponieważ sztuczka jest bardzo prosta.
W pliku CMakeLists.txt zdefiniuj makro, które ma długość ścieżki do twojego projektu na CMake:
# The additional / is important to remove the last character from the path.# Note that it does not matter if the OS uses / or \, because we are only# saving the path size.
string(LENGTH "${CMAKE_SOURCE_DIR}/" SOURCE_PATH_SIZE)
add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")
W kodzie źródłowym zdefiniuj __FILENAME__makro, które po prostu dodaje rozmiar ścieżki źródłowej do __FILE__makra:
Następnie użyj tego nowego makra zamiast __FILE__makra. To działa, ponieważ __FILE__ścieżka zawsze zaczyna się od ścieżki do katalogu źródłowego CMake. Usuwając go z __FILE__łańcucha, preprocesor zajmie się określeniem poprawnej nazwy pliku i wszystko będzie zależne od katalogu głównego projektu CMake.
Jeśli zależy Ci na wydajności, jest to tak samo wydajne, jak użycie __FILE__, ponieważ obie __FILE__i SOURCE_PATH_SIZEsą znanymi stałymi czasu kompilacji, więc kompilator może je zoptymalizować.
Jedynym miejscem, w którym to się nie powiedzie, jest sytuacja, w której używasz tego na wygenerowanych plikach i znajdują się one w folderze kompilacji poza źródłem. Wtedy prawdopodobnie będziesz musiał utworzyć inne makro, używając CMAKE_BUILD_DIRzmiennej zamiast CMAKE_SOURCE_DIR.
Na początku tego nie rozumiałem. Zakodowałem przykłady i porównałem je z gcc i clang i działają. Eksperymentuję też z dodawaniem różnych dosłownych wartości liczbowych i zachowuje się to zgodnie z oczekiwaniami. Wtedy w końcu dotarło do mnie. __FILE__jest wskaźnikiem do tablicy bajtów. Zatem dodanie literału numerycznego jest po prostu dodaniem wskaźnika. Bardzo sprytny @RenatoUtsch
Daniel
Działa tylko wtedy, gdy plik źródłowy znajduje się w katalogu listy cmake. Jeśli plik źródłowy jest na zewnątrz, to się zepsuje, może z dostępem spoza literału. Więc uważaj z tym.
Andry
W rzeczywistości nie zmniejsza to rozmiaru kodu. Myślę, że cała ścieżka jest nadal wkompilowana w plik binarny, tylko wskaźnik jest modyfikowany.
Tarion
Zarówno __FILE__makro, jak i makra SOURCE_PATH_SIZE są stałymi znanymi w czasie kompilacji. Spodziewałbym się, że nowoczesne kompilatory optymalizujące będą w stanie wykryć, że część łańcucha nie jest używana i po prostu usunąć ją z pliku binarnego. W każdym razie nie sądzę, aby te kilka bajtów miało znaczący wpływ na rozmiar binarny, więc nie przejmowałbym się tym.
RenatoUtsch
@RenatoUtsch Projekt, nad którym pracuję, ma zmianę, która po prostu określa nazwę pliku, ale ma tę wadę, że nadaje nazwę pliku C również do nagłówka. Zmiana została wprowadzona w celu uzyskania odtwarzalnych kompilacji. Więc z gcc z -O2, czy łańcuch byłby rzeczywiście zoptymalizowany, a kompilacja odtwarzalna?
Paul Stelian
18
Czysto skompiluj tutaj rozwiązanie czasu. Opiera się na fakcie, że sizeof()literał ciągu znaków zwraca swoją długość + 1.
Możesz swobodnie rozszerzyć kaskadę operatorów warunkowych do maksymalnej rozsądnej nazwy pliku w projekcie. Długość ścieżki nie ma znaczenia, o ile sprawdzasz wystarczająco daleko od końca łańcucha.
Zobaczę, czy uda mi się uzyskać podobne makro bez zakodowanej długości z rekurencją makra ...
Fajna odpowiedź. Musisz użyć co najmniej, -O1aby użyć tego do kompilacji.
Cyfrowa trauma,
1
Czy to nie wstecz? Chcesz znaleźć ostatnie wystąpienie znaku „/”, co oznacza, że najpierw powinieneś zacząć od sizeof(s) > 2sprawdzenia. Również to nie zadziałało w czasie kompilacji dla mnie na -Os. W wyjściowym pliku binarnym były obecne ciągi pełnej ścieżki.
mrtumnus
15
Przynajmniej dla gcc wartością __FILE__jest ścieżka do pliku określona w wierszu poleceń kompilatora . Jeśli kompilujesz w file.cten sposób:
gcc -c /full/path/to/file.c
__FILE__poszerzy się "/full/path/to/file.c". Jeśli zamiast tego zrobisz to:
cd /full/path/to
gcc -c file.c
następnie __FILE__rozwinie się do just "file.c".
Może to być praktyczne lub nie.
Standard C nie wymaga takiego zachowania. Mówi tylko o __FILE__tym, że rozwija się do „Domniemana nazwa bieżącego pliku źródłowego (literał ciągu znaków)”.
Alternatywą jest skorzystanie z #linedyrektywy. Zastępuje bieżący numer wiersza i opcjonalnie nazwę pliku źródłowego. Jeśli chcesz nadpisać nazwę pliku, ale pozostawić sam numer linii, użyj __LINE__makra.
Na przykład możesz dodać to w górnej części file.c:
#line __LINE__ "file.c"
Jedynym problemem jest to, że przypisuje określony numer linii do następnej linii, a pierwszy argument #linemusi być sekwencją cyfr, więc nie możesz zrobić czegoś takiego
#line(__LINE__-1)"file.c"// This is invalid
Zapewnienie, że nazwa pliku w #linedyrektywie jest zgodna z rzeczywistą nazwą pliku, pozostaje jako ćwiczenie.
Przynajmniej w przypadku gcc wpłynie to również na nazwę pliku zgłaszaną w komunikatach diagnostycznych.
Keith Thompson, to świetne rozwiązanie, dziękuję. Jedynym problemem jest to, że wydaje się, że to makro obniża __LINE__wartość o jeden. Tak __LINE__napisane w kolejce xdo oceny x-1. Przynajmniej z gcc 5.4.
elklepo
1
@Klepak: Masz rację i to jest standardowe zachowanie. Dyrektywa „powoduje, że implementacja zachowuje się tak, jakby następująca sekwencja wierszy źródłowych zaczynała się od wiersza źródłowego, który ma numer wiersza określony przez sekwencję cyfr”. Musi to być ciąg cyfr , więc nie możesz go użyć #line __LINE__-1, "file.c". Zaktualizuję odpowiedź.
Keith Thompson
1
Jak by to wyglądało w przypadku innych kompilatorów, takich jak Clang, MSVC lub Intel C Compiler?
Franklin Yu
8
Trochę za późno na imprezę, ale dla GCC spójrz na -ffile-prefix-map=old=newopcję:
Podczas kompilowania plików znajdujących się w katalogu old , zapisz wszelkie odniesienia do nich w wyniku kompilacji, tak jakby pliki znajdowały się w katalogu new . Określenie tej opcji jest równoważne określeniu wszystkich indywidualnych -f*-prefix-mapopcji. Można to wykorzystać do tworzenia odtwarzalnych kompilacji, które są niezależne od lokalizacji. Zobacz także
-fmacro-prefix-mapi -fdebug-prefix-map.
Więc dodam do moich kompilacji Jenkinsa-ffile-prefix-map=${WORKSPACE}/=/ , a drugą, aby usunąć prefiks instalacji lokalnego pakietu deweloperskiego.
UWAGA Niestety -ffile-prefix-mapopcja jest avalable tylko w GCC 8 roku, jak to -fmacro-prefix-map, które myślę , robi __FILE__część. Bo, powiedzmy, GCC 5, mamy tylko to, -fdebug-prefix-mapco nie (wydaje się) mieć wpływ __FILE__.
template<typename T,size_t S>inlineconstexprsize_t get_file_name_offset(const T (& str)[S],size_t i = S -1){return(str[i]=='/'|| str[i]=='\\')? i +1:(i >0? get_file_name_offset(str, i -1):0);}template<typename T>inlineconstexprsize_t get_file_name_offset(T (& str)[1]){return0;}
'
int main(){
printf("%s\n",&__FILE__[get_file_name_offset(__FILE__)]);}
clang: utrzymuje się przy braku oceny czasu kompilacji
Istnieje sztuczka polegająca na zmuszeniu wszystkich 3 kompilatorów do oceny czasu kompilacji, nawet w konfiguracji debugowania z wyłączoną optymalizacją:
namespace utility {template<typename T, T v>struct const_expr_value
{staticconstexprconst T value = v;};}#define UTILITY_CONST_EXPR_VALUE(exp)::utility::const_expr_value<decltype(exp), exp>::value
int main(){
printf("%s\n",&__FILE__[UTILITY_CONST_EXPR_VALUE(get_file_name_offset(__FILE__))]);}
Nie ma sposobu, aby to zrobić. Oczywiście możesz to zrobić w czasie wykonywania, używając środowiska wykonawczego C, jak pokazały niektóre inne odpowiedzi, ale w czasie kompilacji, kiedy uruchamia się pre-procesor, nie masz szczęścia.
strrchrodpowiedź mogłaby przekonująco być obliczane w czasie kompilacji, choć oczywiście nadal nie przez preprocesor. Nie wiem, czy gcc faktycznie to robi, nie sprawdzałem, ale jestem prawie pewien, że oblicza strlenliterały łańcuchowe w czasie kompilacji.
Steve Jessop,
@Steve - może, ale to duża zależność od zachowania konkretnego kompilatora.
Sean,
Nie sądzę, że jest to duża zależność, ponieważ bardzo wątpię, czy ten kod ma krytyczne znaczenie dla wydajności. A jeśli tak, usuń go z pętli. W przypadkach, w których jest to ogromna sprawa, ponieważ absolutnie potrzebujesz literału ciągu zawierającego tylko nazwę basename, być może możesz obliczyć właściwy ciąg w czasie kompilacji , uruchamiając skrypt na źródle.
Steve Jessop,
5
Może nie być krytyczne dla wydajności, ale można je łatwo uznać za krytyczne dla prywatności. Nie ma prawdziwego powodu, aby ujawniać moje praktyki organizacyjne dla poszczególnych klientów w ciągach znaków zamrożonych w opublikowanym pliku EXE. Co gorsza, w przypadku aplikacji utworzonych w imieniu klienta te ciągi mogą ujawniać rzeczy, których mój klient wolałby nie robić, na przykład nie być autorem własnego produktu. Ponieważ __FILE__jest wywoływany niejawnie przez assert(), ten wyciek może wystąpić bez żadnego innego jawnego działania.
RBerteig
@RBerteig, sama nazwa podstawowa __FILE__, może również ujawniać rzeczy, których klient wolałby nie robić , więc używanie __FILE__dowolnego miejsca - niezależnie od tego, czy zawiera pełną absolutną nazwę ścieżki, czy tylko nazwa podstawowa - ma te same problemy, które wskazałeś. W tej sytuacji wszystkie dane wyjściowe będą musiały zostać przeanalizowane i należy wprowadzić specjalny interfejs API, który będzie udostępniony klientom. Reszta wyjścia powinna zostać zrzucona do / dev / NULL lub stdout, a stderr powinno zostać zamknięte. :-)
@mahmood: char file_copy[] = __FILE__; const char *filename = basename(__FILE__);. Powodem kopiowania jest to, że basename może modyfikować ciąg wejściowy. Musisz także uważać, aby wskaźnik wyniku był dobry tylko do momentu basenameponownego wywołania. Oznacza to, że nie jest bezpieczny dla wątków.
Steve Jessop,
@SteveJessop, ah zapomniałem. Prawdziwe.
Prof. Falken,
1
@Amigable: aby być uczciwym, podejrzewam, że basenamew rzeczywistości nie zmodyfikuje ciągu wejściowego, który wynika z __FILE__, ponieważ ciąg wejściowy nie ma /na końcu, więc nie ma potrzeby modyfikacji. Więc może ci się to basenameujrzeć na sucho, ale myślę, że gdy ktoś zobaczy to po raz pierwszy , powinien zobaczyć to ze wszystkimi ograniczeniami.
Steve Jessop,
@SteveJessop Strona podręcznika BSd dla basename () wspomina, że starsza wersja basename () pobiera znak const * i nie modyfikuje ciągu. Strona podręcznika systemu Linux nie wspomina nic o const, ale wspomina, że może zwrócić część łańcucha argumentu. Dlatego najlepiej ostrożnie radzić sobie z basename ().
Prof. Falken,
@SteveJessop, hehe, dopiero teraz, po uważnym przyjrzeniu się twojemu komentarzowi, po czterech latach, zdaję sobie sprawę, że /na końcu napisu basename może mieć dobry powód, aby zmodyfikować swój argument.
Prof. Falken
4
Jeśli używasz CMAKEkompilatora GNU, to globalzdefiniowanie działa dobrze:
-Wno-builtin-macro-redefinedwyciszyć ostrzeżenia kompilatora dotyczące przedefiniowania __FILE__makra.
Te kompilatory nie obsługują tego, zapoznaj się z poniższym sposobem Robust .
Usunięcie ścieżki projektu ze ścieżki do pliku jest Twoim prawdziwym wymaganiem. Nie chcesz tracić czasu, aby dowiedzieć się, gdzie jest header.hplik src/foo/header.hlub src/bar/header.h.
Powinniśmy usunąć __FILE__makro w cmakepliku konfiguracyjnym.
To makro jest używane w większości istniejących kodów. Po prostu przedefiniuj to, aby cię uwolnić.
Kompilatory, takie jak, gccwstępnie definiują to makro na podstawie argumentów wiersza poleceń. Pełna ścieżka jest zapisywana w makefiles wygenerowanych przez cmake.
CMAKE_*_FLAGSWymagany jest twardy kod .
Istnieje kilka poleceń dodających opcje lub definicje kompilatora w niektórych nowszych wersjach, takich jak add_definitions()i add_compile_definitions(). Te polecenia przeanalizują funkcje make, takie jak substprzed zastosowaniem do plików źródłowych. Tego nie chcemy.
Nie działa to w przypadku treści pochodzących z plików dołączanych.
chqrlie
czy możesz wysłać kod? Typowym przypadkiem jest ustawienie zmiennych w zakresie lokalnym i użycie ich w innym.
Levi.G
Na przykład, jeśli używasz __FILE__z definicją do tworzenia diagnostyki w funkcji wbudowanej zdefiniowanej w pliku nagłówkowym, diagnostyka środowiska wykonawczego zgłosi nazwę pliku przekazanego do kompilatora zamiast nazwy pliku dołączanego, podczas gdy numer wiersza będzie odwołaj się do pliku dołączanego.
chqrlie
tak, tak to zostało zaprojektowane, do najczęstszego zastosowania #define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args). kiedy używasz LOG()makra, tak naprawdę nie chcesz widzieć log.hw wiadomościach. w końcu __FILE__makro jest rozwijane w każdym pliku C / Cpp (jednostce kompilacji) zamiast w dołączonych plikach.
Levi.G
3
Niewielką odmianą tego, co zaproponował @ red1ynx, byłoby utworzenie następującego makra:
Zrobiłem makro, __FILENAME__które za każdym razem unika cięcia pełnej ścieżki. Problemem jest przechowywanie wynikowej nazwy pliku w zmiennej cpp-local.
Można to łatwo zrobić, definiując statyczną zmienną globalną w pliku .h . Ta definicja zapewnia oddzielne i niezależne zmienne w każdym pliku .cpp , który zawiera rozszerzenie .h . Aby być odpornym na wielowątkowość, warto sprawić, aby zmienna (e) była również lokalna (TLS) w wątku.
Jedna zmienna przechowuje nazwę pliku (skurczona). Inny ma wartość, która __FILE__dawała niecięta. Plik h:
constchar*GetSourceFileName(constchar* strFilePath,constchar*& rstrFilePathHolder,constchar*& rstrFileNameHolder){if(strFilePath != rstrFilePathHolder){// // This if works in 2 cases: // - when first time called in the cpp (ordinary case) or// - when the macro __FILENAME__ is used in both h and cpp files // and so the method is consequentially called // once with strFilePath == "UserPath/HeaderFileThatUsesMyMACRO.h" and // once with strFilePath == "UserPath/CPPFileThatUsesMyMACRO.cpp"//
rstrFileNameHolder = removePath(strFilePath);
rstrFilePathHolder = strFilePath;}return rstrFileNameHolder;}
RemovePath () można zaimplementować na różne sposoby, ale szybkie i proste wydaje się być dzięki strrchr:
push_macroi pop_macrosą niestandardowe. (gcc obsługuje je w celu zapewnienia zgodności z kompilatorami Microsoft Windows). W każdym razie nie ma sensu wypychać i wstawiać definicji __FILE__; przywrócona wartość i tak nie zostanie użyta po zakończeniu pliku źródłowego. Czystszym sposobem zmiany wartości __FILE__jest#line __LINE__ "foobar.c"
Jeśli znalazłeś się na tej stronie, szukając sposobu na usunięcie bezwzględnej ścieżki źródłowej, która wskazuje brzydką lokalizację kompilacji z pliku binarnego, który wysyłasz, poniżej może odpowiadać Twoim potrzebom.
Chociaż nie daje to dokładnej odpowiedzi, której wyraził autor, ponieważ zakłada użycie CMake , jest dość blisko. Szkoda, że nikt o tym wcześniej nie wspominał, bo zaoszczędziłoby mi to mnóstwo czasu.
OPTION(CMAKE_USE_RELATIVE_PATHS "If true, cmake will use relative paths" ON)
Ustawienie powyższej zmiennej na ONwygeneruje polecenie budowania w formacie:
cd /ugly/absolute/path/to/project/build/src &&
gcc <.. other flags ..>-c ../../src/path/to/source.c
W rezultacie __FILE__makro rozwiąże problem../../src/path/to/source.c
Dokumentacja CMake 3.4+ stwierdza, że „Ta zmienna nie ma wpływu. Częściowo zaimplementowany efekt, jaki miała w poprzednich wersjach, został usunięty w CMake 3.4”. Jeśli zadziałało z 3.13, jest inny powód.
__BASE_FILE__To makro jest interpretowane jako nazwa głównego pliku wejściowego w postaci stałej w języku C. To jest plik źródłowy określony w wierszu poleceń preprocesora lub kompilatora C.
a następnie kontroluj sposób wyświetlania nazwy pliku, zmieniając reprezentację pliku źródłowego (pełna ścieżka / ścieżka względna / nazwa podstawowa) w czasie kompilacji.
nie robi różnicy. Użyłem __FILE__i __BASE_FILE__jednak oba pokazują pełną ścieżkę do pliku
mahmood
jak wywołać kompilator?
ziu
2
Wtedy założę się, że SCONS dzwoni do gcc w ten sposób gcc /absolute/path/to/file.c. Jeśli znajdziesz sposób na zmianę tego zachowania (otwierając kolejne pytanie na SO, lol?), Nie musisz modyfikować ciągu w czasie wykonywania
ziu
17
Ta odpowiedź jest w 100% błędna. __BASE_FILE__(jak mówią dokumenty, choć niewyraźnie) tworzy nazwę pliku podanego w linii poleceń , np. test.club w /tmp/test.czależności od tego, jak wywołałeś kompilator. To jest dokładnie to samo co __FILE__, z wyjątkiem sytuacji, gdy jesteś w pliku nagłówkowym, w którym to przypadku __FILE__tworzy nazwę bieżącego pliku (np. foo.h), Podczas gdy __BASE_FILE__kontynuuje tworzenie test.c.
Quuxplusone
0
Oto rozwiązanie, które działa w środowiskach, które nie mają biblioteki ciągów (jądro Linuksa, systemy wbudowane itp.):
Może chcieć zaznaczyć „/” i „\”, w zależności od platformy.
Technophile
0
#include<algorithm>#include<string>usingnamespace std;
string f( __FILE__ );
f = string((find(f.rbegin(), f.rend(),'/')+1).base()+1, f.end());// searches for the '/' from the back, transfers the reverse iterator // into a forward iterator and constructs a new sting with both
__FILE__
zawiera, zmieniając nazwę pliku, którą podajesz w linii poleceń. Więc zamiast tegogcc /full/path/to/file.c
spróbujcd /full/path/to; gcc file.c; cd -;
. Oczywiście jest to trochę więcej, jeśli polegasz na bieżącym katalogu gcc jako ścieżki dołączania lub lokalizacji pliku wyjściowego. Edycja: dokumentacja gcc sugeruje, że jest to pełna ścieżka, a nie argument nazwy pliku wejściowego, ale to nie jest to, co widzę dla gcc 4.5.3 na Cygwin. Więc równie dobrze możesz wypróbować go w systemie Linux i zobaczyć.__FILE__
wydrukowany jako../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.c
i chcę wiedzieć, gdzie gcc skompilował plik w taki sposób, że ten plik jest względnie zlokalizowany, tak jak pokazano.Odpowiedzi:
Próbować
W systemie Windows użyj „\\” zamiast „/”.
źródło
/
jest prawidłowym separatorem ścieżek w systemie Windows./
jest prawidłowym separatorem ścieżki w nazwach plików przekazywanych do funkcji CreateFile () i tak dalej. Jednak nie zawsze oznacza to, że można używać/
dowolnego miejsca w systemie Windows, ponieważ istnieje tradycja (odziedziczona z CPM) używania/
jako argumentu prowadzącego w wierszu polecenia. Ale narzędziem jakość byłby ostrożny w nazwach plików podziału na obu slash i znaki backslash, aby uniknąć wszelkich problemów dla ludzi, że uda się wykorzystać/
.char* fileName = basename(__FILE__);
na pewno jest dostępna w systemie Linux i OS X, ale nie wiem o systemie Windows.strrchr("/" __FILE__, '/') + 1
. Poprzez poprzedzanie „/” na__FILE__
, strrchr ma pewność, że coś znajdzie, a zatem warunek?: Nie jest już potrzebny.Oto wskazówka, jeśli używasz cmake. Z: http://public.kitware.com/pipermail/cmake/2013-J January 053117.html
Kopiuję wskazówkę, więc wszystko jest na tej stronie:
Jeśli używasz make GNU, nie widzę powodu, dla którego nie mógłbyś rozszerzyć tego na własne pliki makefile. Na przykład możesz mieć taką linię:
gdzie
$(SOURCE_PREFIX)
jest prefiks, który chcesz usunąć.Następnie użyj
__FILENAME__
zamiast__FILE__
.źródło
__FILE__
nie jest prostym symbolem preprocesora, zmiany w bieżącym pliku są często używane do emisji nazwy bieżącego pliku (nagłówka lub modułu źródłowego). To__FILENAME__
musiałby tylko źródło zewnętrzną__FILE__
wszędzie wypada z przestrzeni kodu.Właśnie wymyśliłem świetne rozwiązanie, które działa zarówno z plikami źródłowymi, jak i nagłówkowymi, jest bardzo wydajne i działa w czasie kompilacji na wszystkich platformach bez rozszerzeń specyficznych dla kompilatora. To rozwiązanie zachowuje również względną strukturę katalogów projektu, dzięki czemu wiesz, w którym folderze znajduje się plik, i tylko w odniesieniu do katalogu głównego projektu.
Chodzi o to, aby uzyskać rozmiar katalogu źródłowego za pomocą narzędzia do budowania i po prostu dodać go do
__FILE__
makra, całkowicie usuwając katalog i wyświetlając tylko nazwę pliku, zaczynając od katalogu źródłowego.Poniższy przykład jest zaimplementowany przy użyciu CMake, ale nie ma powodu, dla którego nie działałby z innymi narzędziami do kompilacji, ponieważ sztuczka jest bardzo prosta.
W pliku CMakeLists.txt zdefiniuj makro, które ma długość ścieżki do twojego projektu na CMake:
W kodzie źródłowym zdefiniuj
__FILENAME__
makro, które po prostu dodaje rozmiar ścieżki źródłowej do__FILE__
makra:Następnie użyj tego nowego makra zamiast
__FILE__
makra. To działa, ponieważ__FILE__
ścieżka zawsze zaczyna się od ścieżki do katalogu źródłowego CMake. Usuwając go z__FILE__
łańcucha, preprocesor zajmie się określeniem poprawnej nazwy pliku i wszystko będzie zależne od katalogu głównego projektu CMake.Jeśli zależy Ci na wydajności, jest to tak samo wydajne, jak użycie
__FILE__
, ponieważ obie__FILE__
iSOURCE_PATH_SIZE
są znanymi stałymi czasu kompilacji, więc kompilator może je zoptymalizować.Jedynym miejscem, w którym to się nie powiedzie, jest sytuacja, w której używasz tego na wygenerowanych plikach i znajdują się one w folderze kompilacji poza źródłem. Wtedy prawdopodobnie będziesz musiał utworzyć inne makro, używając
CMAKE_BUILD_DIR
zmiennej zamiastCMAKE_SOURCE_DIR
.źródło
__FILE__
jest wskaźnikiem do tablicy bajtów. Zatem dodanie literału numerycznego jest po prostu dodaniem wskaźnika. Bardzo sprytny @RenatoUtsch__FILE__
makro, jak i makra SOURCE_PATH_SIZE są stałymi znanymi w czasie kompilacji. Spodziewałbym się, że nowoczesne kompilatory optymalizujące będą w stanie wykryć, że część łańcucha nie jest używana i po prostu usunąć ją z pliku binarnego. W każdym razie nie sądzę, aby te kilka bajtów miało znaczący wpływ na rozmiar binarny, więc nie przejmowałbym się tym.Czysto skompiluj tutaj rozwiązanie czasu. Opiera się na fakcie, że
sizeof()
literał ciągu znaków zwraca swoją długość + 1.Możesz swobodnie rozszerzyć kaskadę operatorów warunkowych do maksymalnej rozsądnej nazwy pliku w projekcie. Długość ścieżki nie ma znaczenia, o ile sprawdzasz wystarczająco daleko od końca łańcucha.
Zobaczę, czy uda mi się uzyskać podobne makro bez zakodowanej długości z rekurencją makra ...
źródło
-O1
aby użyć tego do kompilacji.sizeof(s) > 2
sprawdzenia. Również to nie zadziałało w czasie kompilacji dla mnie na -Os. W wyjściowym pliku binarnym były obecne ciągi pełnej ścieżki.Przynajmniej dla gcc wartością
__FILE__
jest ścieżka do pliku określona w wierszu poleceń kompilatora . Jeśli kompilujesz wfile.c
ten sposób:__FILE__
poszerzy się"/full/path/to/file.c"
. Jeśli zamiast tego zrobisz to:następnie
__FILE__
rozwinie się do just"file.c"
.Może to być praktyczne lub nie.
Standard C nie wymaga takiego zachowania. Mówi tylko o
__FILE__
tym, że rozwija się do „Domniemana nazwa bieżącego pliku źródłowego (literał ciągu znaków)”.Alternatywą jest skorzystanie z
#line
dyrektywy. Zastępuje bieżący numer wiersza i opcjonalnie nazwę pliku źródłowego. Jeśli chcesz nadpisać nazwę pliku, ale pozostawić sam numer linii, użyj__LINE__
makra.Na przykład możesz dodać to w górnej części
file.c
:Jedynym problemem jest to, że przypisuje określony numer linii do następnej linii, a pierwszy argument
#line
musi być sekwencją cyfr, więc nie możesz zrobić czegoś takiegoZapewnienie, że nazwa pliku w
#line
dyrektywie jest zgodna z rzeczywistą nazwą pliku, pozostaje jako ćwiczenie.Przynajmniej w przypadku gcc wpłynie to również na nazwę pliku zgłaszaną w komunikatach diagnostycznych.
źródło
__LINE__
wartość o jeden. Tak__LINE__
napisane w kolejcex
do ocenyx-1
. Przynajmniej z gcc 5.4.#line __LINE__-1, "file.c"
. Zaktualizuję odpowiedź.Trochę za późno na imprezę, ale dla GCC spójrz na
-ffile-prefix-map=old=new
opcję:Więc dodam do moich kompilacji Jenkinsa
-ffile-prefix-map=${WORKSPACE}/=/
, a drugą, aby usunąć prefiks instalacji lokalnego pakietu deweloperskiego.UWAGA Niestety
-ffile-prefix-map
opcja jest avalable tylko w GCC 8 roku, jak to-fmacro-prefix-map
, które myślę , robi__FILE__
część. Bo, powiedzmy, GCC 5, mamy tylko to,-fdebug-prefix-map
co nie (wydaje się) mieć wpływ__FILE__
.źródło
-ffile-prefix-map
rzeczywiście oznacza obie opcje-fdebug-prefix-map
i-fmacro-prefix-map
. Zobacz także odniesienia na reproducible-builds.org/docs/build-path Błąd GCC, który śledzi-fmacro-prefix-map
i-ffile-prefix-map
jest gcc.gnu.org/bugzilla/show_bug.cgi?id=70268msvc2015u3, gcc5.4, clang3.8.0
'
Kod generuje przesunięcie czasu kompilacji, gdy:
gcc
: co najmniej gcc6.1 +-O1
msvc
: wstaw wynik do zmiennej constexpr:clang
: utrzymuje się przy braku oceny czasu kompilacjiIstnieje sztuczka polegająca na zmuszeniu wszystkich 3 kompilatorów do oceny czasu kompilacji, nawet w konfiguracji debugowania z wyłączoną optymalizacją:
https://godbolt.org/z/u6s8j3
źródło
W VC, podczas używania
/FC
,__FILE__
rozwija do pełnej ścieżki, bez/FC
opcji__FILE__
rozwija nazwę pliku. ref: tutajźródło
Nie ma sposobu, aby to zrobić. Oczywiście możesz to zrobić w czasie wykonywania, używając środowiska wykonawczego C, jak pokazały niektóre inne odpowiedzi, ale w czasie kompilacji, kiedy uruchamia się pre-procesor, nie masz szczęścia.
źródło
strrchr
odpowiedź mogłaby przekonująco być obliczane w czasie kompilacji, choć oczywiście nadal nie przez preprocesor. Nie wiem, czy gcc faktycznie to robi, nie sprawdzałem, ale jestem prawie pewien, że obliczastrlen
literały łańcuchowe w czasie kompilacji.__FILE__
jest wywoływany niejawnie przezassert()
, ten wyciek może wystąpić bez żadnego innego jawnego działania.__FILE__
, może również ujawniać rzeczy, których klient wolałby nie robić , więc używanie__FILE__
dowolnego miejsca - niezależnie od tego, czy zawiera pełną absolutną nazwę ścieżki, czy tylko nazwa podstawowa - ma te same problemy, które wskazałeś. W tej sytuacji wszystkie dane wyjściowe będą musiały zostać przeanalizowane i należy wprowadzić specjalny interfejs API, który będzie udostępniony klientom. Reszta wyjścia powinna zostać zrzucona do / dev / NULL lub stdout, a stderr powinno zostać zamknięte. :-)Użyj funkcji basename () lub, jeśli pracujesz w systemie Windows, _splitpath () .
Spróbuj także
man 3 basename
w muszli.źródło
char file_copy[] = __FILE__; const char *filename = basename(__FILE__);
. Powodem kopiowania jest to, że basename może modyfikować ciąg wejściowy. Musisz także uważać, aby wskaźnik wyniku był dobry tylko do momentubasename
ponownego wywołania. Oznacza to, że nie jest bezpieczny dla wątków.basename
w rzeczywistości nie zmodyfikuje ciągu wejściowego, który wynika z__FILE__
, ponieważ ciąg wejściowy nie ma/
na końcu, więc nie ma potrzeby modyfikacji. Więc może ci się tobasename
ujrzeć na sucho, ale myślę, że gdy ktoś zobaczy to po raz pierwszy , powinien zobaczyć to ze wszystkimi ograniczeniami./
na końcu napisu basename może mieć dobry powód, aby zmodyfikować swój argument.Jeśli używasz
CMAKE
kompilatora GNU, toglobal
zdefiniowanie działa dobrze:źródło
Muszę użyć tego samego rozwiązania z @Patrick jest odpowiedzią na lata.
Ma mały problem, gdy pełna ścieżka zawiera łącze symboliczne.
Lepsze rozwiązanie.
Dlaczego warto tego używać?
-Wno-builtin-macro-redefined
wyciszyć ostrzeżenia kompilatora dotyczące przedefiniowania__FILE__
makra.Usunięcie ścieżki projektu ze ścieżki do pliku jest Twoim prawdziwym wymaganiem. Nie chcesz tracić czasu, aby dowiedzieć się, gdzie jest
header.h
pliksrc/foo/header.h
lubsrc/bar/header.h
.Powinniśmy usunąć
__FILE__
makro wcmake
pliku konfiguracyjnym.To makro jest używane w większości istniejących kodów. Po prostu przedefiniuj to, aby cię uwolnić.
Kompilatory, takie jak,
gcc
wstępnie definiują to makro na podstawie argumentów wiersza poleceń. Pełna ścieżka jest zapisywana wmakefile
s wygenerowanych przezcmake
.CMAKE_*_FLAGS
Wymagany jest twardy kod .Istnieje kilka poleceń dodających opcje lub definicje kompilatora w niektórych nowszych wersjach, takich jak
add_definitions()
iadd_compile_definitions()
. Te polecenia przeanalizują funkcje make, takie jaksubst
przed zastosowaniem do plików źródłowych. Tego nie chcemy.Solidny sposób na
-Wno-builtin-macro-redefined
.źródło
__FILE__
z definicją do tworzenia diagnostyki w funkcji wbudowanej zdefiniowanej w pliku nagłówkowym, diagnostyka środowiska wykonawczego zgłosi nazwę pliku przekazanego do kompilatora zamiast nazwy pliku dołączanego, podczas gdy numer wiersza będzie odwołaj się do pliku dołączanego.#define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args)
. kiedy używaszLOG()
makra, tak naprawdę nie chcesz widziećlog.h
w wiadomościach. w końcu__FILE__
makro jest rozwijane w każdym pliku C / Cpp (jednostce kompilacji) zamiast w dołączonych plikach.Niewielką odmianą tego, co zaproponował @ red1ynx, byłoby utworzenie następującego makra:
W każdym z plików .c (pp) dodaj:
Następnie możesz odwołać się
THIS_FILE_NAME
zamiast__FILE__
:Oznacza to, że konstrukcja jest wykonywana raz na plik .c (pp), a nie za każdym razem, gdy odwołuje się do makra.
Jest ograniczone do używania tylko z plików .c (pp) i byłoby bezużyteczne z plików nagłówkowych.
źródło
Zrobiłem makro,
__FILENAME__
które za każdym razem unika cięcia pełnej ścieżki. Problemem jest przechowywanie wynikowej nazwy pliku w zmiennej cpp-local.Można to łatwo zrobić, definiując statyczną zmienną globalną w pliku .h . Ta definicja zapewnia oddzielne i niezależne zmienne w każdym pliku .cpp , który zawiera rozszerzenie .h . Aby być odpornym na wielowątkowość, warto sprawić, aby zmienna (e) była również lokalna (TLS) w wątku.
Jedna zmienna przechowuje nazwę pliku (skurczona). Inny ma wartość, która
__FILE__
dawała niecięta. Plik h:Samo makro wywołuje metodę z całą logiką:
A funkcja jest realizowana w ten sposób:
RemovePath () można zaimplementować na różne sposoby, ale szybkie i proste wydaje się być dzięki strrchr:
źródło
Najnowszy kompilator Clang ma
__FILE_NAME__
makro (patrz tutaj ).źródło
mam tylko nadzieję, że poprawię trochę makro FILE:
#define FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
to łapie / i \, jak zażądał Czarek Tomczak, a to działa świetnie w moim mieszanym środowisku.
źródło
FILE
to naprawdę zły pomysł, jeśli włączysz<stdio.h>
.Próbować
po instrukcjach include w pliku źródłowym i dodaj
na końcu pliku źródłowego.
źródło
push_macro
ipop_macro
są niestandardowe. (gcc obsługuje je w celu zapewnienia zgodności z kompilatorami Microsoft Windows). W każdym razie nie ma sensu wypychać i wstawiać definicji__FILE__
; przywrócona wartość i tak nie zostanie użyta po zakończeniu pliku źródłowego. Czystszym sposobem zmiany wartości__FILE__
jest#line __LINE__ "foobar.c"
Oto przenośna funkcja, która działa zarówno w systemie Linux (ścieżka „/”), jak i Windows (połączenie „\” i „/”).
Kompiluje się z gcc, clang i vs.
Wyjście standardowe:
źródło
Oto rozwiązanie, które wykorzystuje obliczenia czasu kompilacji:
źródło
Jeśli znalazłeś się na tej stronie, szukając sposobu na usunięcie bezwzględnej ścieżki źródłowej, która wskazuje brzydką lokalizację kompilacji z pliku binarnego, który wysyłasz, poniżej może odpowiadać Twoim potrzebom.
Chociaż nie daje to dokładnej odpowiedzi, której wyraził autor, ponieważ zakłada użycie CMake , jest dość blisko. Szkoda, że nikt o tym wcześniej nie wspominał, bo zaoszczędziłoby mi to mnóstwo czasu.
Ustawienie powyższej zmiennej na
ON
wygeneruje polecenie budowania w formacie:W rezultacie
__FILE__
makro rozwiąże problem../../src/path/to/source.c
Dokumentacja CMake
Uważaj jednak na ostrzeżenie na stronie dokumentacji:
Nie ma gwarancji, że zadziała we wszystkich przypadkach, ale zadziałał w moim - CMake 3.13 + gcc 4.5
źródło
Ponieważ używasz GCC, możesz skorzystać z
a następnie kontroluj sposób wyświetlania nazwy pliku, zmieniając reprezentację pliku źródłowego (pełna ścieżka / ścieżka względna / nazwa podstawowa) w czasie kompilacji.
źródło
__FILE__
i__BASE_FILE__
jednak oba pokazują pełną ścieżkę do plikugcc /absolute/path/to/file.c
. Jeśli znajdziesz sposób na zmianę tego zachowania (otwierając kolejne pytanie na SO, lol?), Nie musisz modyfikować ciągu w czasie wykonywania__BASE_FILE__
(jak mówią dokumenty, choć niewyraźnie) tworzy nazwę pliku podanego w linii poleceń , np.test.c
lub w/tmp/test.c
zależności od tego, jak wywołałeś kompilator. To jest dokładnie to samo co__FILE__
, z wyjątkiem sytuacji, gdy jesteś w pliku nagłówkowym, w którym to przypadku__FILE__
tworzy nazwę bieżącego pliku (np.foo.h
), Podczas gdy__BASE_FILE__
kontynuuje tworzenietest.c
.Oto rozwiązanie, które działa w środowiskach, które nie mają biblioteki ciągów (jądro Linuksa, systemy wbudowane itp.):
Teraz po prostu użyj
FILENAME
zamiast__FILENAME__
. Tak, nadal jest to element środowiska uruchomieniowego, ale działa.źródło
źródło
Krótka, działająca odpowiedź zarówno dla systemu Windows, jak i * nix:
źródło
std::max<const char*>
zamiast tylkostd::max
?