Akan sangat bagus untuk menemukan solusi preprosesor saja. Saya takut bahwa saran berdasarkan operasi string akan dijalankan pada saat runtime.
cdleonard
9
Karena Anda menggunakan gcc, saya pikir Anda dapat mengubah isi __FILE__dengan mengubah nama file yang Anda berikan pada baris perintah. Jadi alih-alih gcc /full/path/to/file.c, coba cd /full/path/to; gcc file.c; cd -;. Tentu saja ada sedikit lebih dari itu jika Anda mengandalkan direktori gcc saat ini untuk jalur sertakan atau lokasi file output. Sunting: gcc docs menyarankan bahwa ini adalah path lengkap, bukan argumen nama file input, tapi bukan itu yang saya lihat untuk gcc 4.5.3 di Cygwin. Jadi Anda bisa mencobanya di Linux dan lihat.
Steve Jessop
4
GCC 4.5.1 (dibuat khusus untuk arm-none-eabi) menggunakan teks persis nama file pada baris perintahnya. Dalam kasus saya itu adalah kesalahan IDE untuk memanggil GCC dengan semua nama file yang memenuhi syarat alih-alih menempatkan direktori saat ini di tempat yang masuk akal (lokasi file proyek, mungkin?) Atau dapat dikonfigurasi dan menggunakan jalur relatif dari sana. Saya menduga banyak IDE melakukan itu (terutama pada Windows) karena beberapa jenis ketidaknyamanan terkait dengan menjelaskan di mana direktori "saat ini" sebenarnya untuk aplikasi GUI.
RBerteig
3
@SteveJessop - harap Anda membaca komentar ini. Saya memiliki situasi di mana saya melihat __FILE__dicetak sebagai ../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.cdan saya ingin tahu di mana gcc mengkompilasi file sehingga file ini relatif terletak seperti yang ditunjukkan.
Chan Kim
1
Pertanyaan ini bukan tipuan dari yang ditautkan. Untuk satu, yang terkait adalah tentang C ++, dan jawabannya akibatnya menggali ke dalam C ++ macro esoterica. Kedua, tidak ada dalam pertanyaan OP yang mengamanatkan solusi makro. Itu hanya menunjukkan masalah dan mengajukan pertanyaan terbuka.
/adalah pemisah jalur yang valid dalam nama file yang diteruskan ke CreateFile () dan sebagainya. Namun, itu tidak selalu berarti bahwa Anda dapat menggunakan /sembarang di Windows karena ada tradisi (diwarisi dari CPM) menggunakan /sebagai argumen memimpin pada prompt perintah. Tapi alat berkualitas akan berhati-hati untuk nama file perpecahan di kedua slash dan karakter backslash untuk menghindari masalah untuk orang-orang yang mengelola untuk menggunakan /.
RBerteig
5
@AmigableClarkKant, tidak, Anda dapat mencampur kedua pemisah dengan nama file yang sama.
RBerteig
2
Jika platform Anda mendukungnya, itu char* fileName = basename(__FILE__); pasti ada di Linux dan OS X, tidak tahu tentang Windows.
JeremyP
14
Ini bisa disingkat menjadi strrchr("/" __FILE__, '/') + 1. Dengan menambahkan "/" ke __FILE__, strrchr dijamin untuk menemukan sesuatu, dan dengan demikian syarat:: tidak lagi diperlukan.
Jika Anda menggunakan GNU make, saya tidak melihat alasan Anda tidak dapat memperluas ini ke makefile Anda sendiri. Misalnya, Anda mungkin memiliki garis seperti ini:
Saya khawatir ini tidak berfungsi untuk FILE yang dirujuk dalam file header.
Baiyan Huang
2
Setuju dengan @BaiyanHuang tetapi tidak yakin bahwa komentarnya jelas. __FILE__bukan simbol preprocessor sederhana, itu berubah menjadi file saat ini sering digunakan untuk memancarkan nama file saat ini (header atau modul sumber). Ini __FILENAME__hanya akan memiliki sumber terluar
nhed
3
Solusi ini tidak portabel karena menggunakan shell Bourne escapes. Lebih baik menggunakan CMake untuk mengimplementasikannya dengan cara yang bersih dan portabel. Lihat define_file_basename_for_sources jawaban makro di sini.
Colin D Bennett
3
Varian GNU Make dari ini adalah CFLAGS + = -D__FILENAME __ = \ "$ (notdir $ <) \"
ctuffli
4
@ firegurafiku Pada platform yang disematkan, ukuran kode seringkali lebih merupakan kendala daripada kecepatan. Jika Anda memiliki pernyataan debug di setiap file, itu dapat dengan cepat menggembungkan ukuran file dengan string jalur penuh. Saya sedang berurusan dengan platform seperti saat ini, dan referensi di __FILE__mana-mana keluar dari ruang kode.
mrtumnus
26
Saya baru saja memikirkan solusi hebat untuk ini yang bekerja dengan kedua sumber dan file header, sangat efisien dan bekerja pada waktu kompilasi di semua platform tanpa ekstensi spesifik kompiler. Solusi ini juga mempertahankan struktur direktori relatif dari proyek Anda, sehingga Anda tahu di folder mana file itu berada, dan hanya relatif terhadap akar proyek Anda.
Idenya adalah untuk mendapatkan ukuran direktori sumber dengan alat build Anda dan tambahkan saja ke __FILE__ makro, menghapus direktori sepenuhnya dan hanya menampilkan nama file mulai dari direktori sumber Anda.
Contoh berikut diimplementasikan menggunakan CMake, tetapi tidak ada alasan itu tidak akan bekerja dengan alat build lain, karena triknya sangat sederhana.
Pada file CMakeLists.txt, tentukan makro yang memiliki panjang jalur ke proyek Anda di 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}")
Pada kode sumber Anda, tentukan __FILENAME__makro yang hanya menambahkan ukuran jalur sumber ke __FILE__makro:
Kemudian gunakan makro baru ini bukan __FILE__makro. Ini berfungsi karena __FILE__path akan selalu dimulai dengan path ke dir sumber CMake Anda. Dengan menghapusnya dari __FILE__string, preprocessor akan menentukan nama file yang benar dan itu semua akan relatif terhadap akar proyek CMake Anda.
Jika Anda peduli dengan kinerja, ini seefisien menggunakan __FILE__ , karena keduanya __FILE__dan SOURCE_PATH_SIZEdikenal sebagai konstanta waktu kompilasi, sehingga dapat dioptimalkan oleh kompiler.
Satu-satunya tempat di mana ini akan gagal adalah jika Anda menggunakan ini pada file yang dihasilkan dan mereka berada di folder build off-source. Maka Anda mungkin harus membuat makro lain menggunakan CMAKE_BUILD_DIRvariabel, bukan CMAKE_SOURCE_DIR.
Saya tidak mengerti ini pada awalnya. Saya membuat kode contoh dan menjalankannya terhadap gcc dan dentang dan mereka bekerja. Saya juga bereksperimen dengan hanya menambahkan berbagai nilai numerik literal, dan itu berlaku seperti yang saya harapkan. Kemudian akhirnya saya sadar. __FILE__adalah pointer ke array byte. Jadi menambahkan literal numerik hanyalah penambahan pointer. Sangat pintar @RenatoUtsch
Daniel
Ini hanya berfungsi jika file sumber berada di bawah direktori cmake list. Jika file sumber di luar maka itu akan pecah, mungkin dengan akses di luar string literal. Jadi berhati-hatilah dengan itu.
Andry
Ini sebenarnya tidak mengurangi ukuran kode. Saya kira seluruh path masih dikompilasi ke dalam biner, hanya pointer yang dimodifikasi.
Tarion
Baik __FILE__makro dan makro SOURCE_PATH_SIZE adalah konstanta diketahui pada waktu kompilasi. Saya berharap kompiler optimisasi modern dapat mendeteksi bahwa bagian dari string tidak digunakan dan hanya menghapusnya dari biner. Bagaimanapun, saya tidak berpikir beberapa byte ini akan membuat perbedaan yang signifikan dalam ukuran biner, jadi saya tidak akan terlalu peduli tentang itu.
RenatoUtsch
@RenatoUtsch Proyek yang sedang saya kerjakan memiliki perubahan yang hanya menentukan nama file, tetapi memiliki kelemahan memberikan nama file C ke header juga. Perubahan dilakukan untuk mendapatkan bangunan yang dapat direproduksi. Jadi dengan gcc dengan -O2, apakah string memang dioptimalkan dan build dibuat direproduksi?
Paul Stelian
18
Solusi waktu murni kompilasi di sini. Ini didasarkan pada fakta bahwa sizeof()string literal mengembalikan panjangnya + 1.
Jangan ragu untuk memperluas kaskade operator bersyarat ke nama file maksimum yang masuk akal dalam proyek. Panjang jalur tidak masalah, selama Anda memeriksa cukup jauh dari ujung string.
Saya akan melihat apakah saya bisa mendapatkan makro yang sama tanpa panjang kode keras dengan rekursi makro ...
Jawaban keren Anda harus menggunakan setidaknya -O1untuk menggunakan ini agar waktu kompilasi.
Digital Trauma
1
Bukankah ini terbalik? Anda ingin menemukan kemunculan terakhir dari '/', yang berarti Anda harus mulai dengan sizeof(s) > 2cek terlebih dahulu. Juga, ini tidak bekerja pada waktu kompilasi untuk saya, di -Os. String path lengkap hadir dalam biner output.
mrtumnus
15
Paling tidak untuk gcc, nilai __FILE__path file seperti yang ditentukan pada baris perintah kompiler . Jika Anda mengompilasi file.cseperti ini:
gcc -c /full/path/to/file.c
yang __FILE__akan memperluas ke "/full/path/to/file.c". Jika Anda melakukan ini:
cd /full/path/to
gcc -c file.c
kemudian __FILE__akan berkembang menjadi adil "file.c".
Ini mungkin atau mungkin tidak praktis.
Standar C tidak memerlukan perilaku ini. Yang dikatakannya __FILE__hanyalah memperluas ke "Nama yang diduga dari fi le sumber saat ini (karakter string literal)".
Alternatifnya adalah dengan menggunakan #linearahan. Itu menimpa nomor baris saat ini, dan opsional nama file sumber. Jika Anda ingin mengganti nama file tetapi meninggalkan nomor baris sendiri, gunakan __LINE__makro.
Misalnya, Anda dapat menambahkan ini di dekat bagian atas file.c:
#line __LINE__ "file.c"
Satu-satunya masalah dengan ini adalah bahwa ia memberikan nomor baris yang ditentukan ke baris berikut , dan argumen pertama #lineharus berupa urutan digit sehingga Anda tidak dapat melakukan sesuatu seperti
#line(__LINE__-1)"file.c"// This is invalid
Memastikan bahwa nama file dalam #linearahan cocok dengan nama sebenarnya file tersebut dibiarkan sebagai latihan.
Setidaknya untuk gcc, ini juga akan memengaruhi nama file yang dilaporkan dalam pesan diagnostik.
Keith Thompson, ini solusi hebat, terima kasih. Satu-satunya masalah adalah tampaknya makro ini memotong __LINE__nilai satu. Jadi __LINE__ditulis sesuai garis xdievaluasi x-1. Setidaknya dengan gcc 5.4.
elklepo
1
@ Klepak: Anda benar, dan itu perilaku standar. Arahan "menyebabkan implementasi berperilaku seolah-olah urutan baris sumber berikut dimulai dengan baris sumber yang memiliki nomor baris seperti yang ditentukan oleh urutan digit". Dan itu harus berupa digit-urutan , jadi Anda tidak dapat menggunakan #line __LINE__-1, "file.c". Saya akan memperbarui jawaban saya.
Keith Thompson
1
Bagaimana jadinya untuk kompiler lain seperti Dentang, MSVC, atau Intel C Compiler?
Franklin Yu
8
Agak terlambat ke pesta, tetapi untuk GCC lihat -ffile-prefix-map=old=newopsi:
Ketika mengkompilasi file yang berada di direktori lama , catat referensi apa pun untuk mereka di hasil kompilasi seolah-olah file tersebut berada di direktori yang baru . Menentukan opsi ini sama dengan menentukan semua -f*-prefix-mapopsi individual . Ini dapat digunakan untuk membuat bangunan yang dapat direproduksi yang independen terhadap lokasi. Lihat juga
-fmacro-prefix-mapdan -fdebug-prefix-map.
Jadi untuk build Jenkins saya akan saya tambahkan -ffile-prefix-map=${WORKSPACE}/=/, dan yang lain untuk menghapus awalan instal paket dev lokal.
CATATAN Sayangnya -ffile-prefix-mapopsi ini hanya tersedia dalam GCC 8 dan seterusnya, seperti -fmacro-prefix-mapyang, saya pikir , melakukan __FILE__bagiannya. Karena, katakanlah, GCC 5, kami hanya memiliki -fdebug-prefix-mapyang tidak (tampaknya) memengaruhi __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: tetap ada pada tidak menyusun evaluasi waktu
Ada trik untuk memaksa ketiga kompiler melakukan kompilasi evaluasi waktu bahkan dalam konfigurasi debug dengan optimasi yang dinonaktifkan:
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__))]);}
Tidak ada waktu kompilasi cara untuk melakukan ini. Jelas Anda dapat melakukannya saat runtime menggunakan runtime C, seperti yang ditunjukkan beberapa jawaban lainnya, tetapi pada waktu kompilasi, ketika pre-procesor masuk, Anda kurang beruntung.
yang strrchrjawabannya bisa masuk akal dihitung pada saat kompilasi, walaupun tentu saja masih belum oleh preprocessor. Saya tidak tahu apakah gcc benar-benar melakukannya, saya belum memeriksa, tapi saya cukup yakin bahwa itu menghitung strlenstring literal pada waktu kompilasi.
Steve Jessop
@Steve - mungkin, tapi itu ketergantungan besar pada perilaku spesifik kompiler.
Sean
Saya rasa ini bukan ketergantungan besar, karena saya sangat meragukan bahwa kode ini kritis terhadap kinerja. Dan jika ya, pindahkan dari loop. Dalam kasus di mana ini adalah masalah besar, karena Anda benar-benar membutuhkan string literal yang hanya berisi nama dasar, Anda mungkin dapat menghitung string yang tepat saat membangun dengan menjalankan beberapa skrip di atas sumber.
Steve Jessop
5
Ini mungkin bukan kinerja yang kritis, tetapi dapat dengan mudah dilihat sebagai privasi yang kritis. Tidak ada alasan bagus untuk mengungkapkan praktik organisasi per pelanggan saya di string yang dibekukan ke dalam file EXE yang dirilis. Lebih buruk lagi, untuk aplikasi yang dibuat atas nama pelanggan, string itu mungkin mengungkapkan hal-hal yang pelanggan saya mungkin tidak suka, seperti tidak menjadi penulis produk mereka sendiri. Karena __FILE__dipanggil secara implisit oleh assert(), kebocoran ini dapat terjadi tanpa tindakan nyata apa pun.
RBerteig
@RBerteig sendiri nama dasarnya __FILE__juga dapat mengungkapkan hal-hal yang mungkin tidak diinginkan pelanggan, jadi gunakan __FILE__di mana saja - apakah itu berisi pathname absolut penuh atau hanya nama dasarnya - memiliki masalah yang sama seperti yang Anda tunjukkan. Dalam situasi ini semua output perlu diteliti dan API khusus harus diperkenalkan untuk output kepada pelanggan. Sisa output harus dibuang ke / dev / NULL atau stdout dan stderr harus ditutup. :-)
@mahmood: char file_copy[] = __FILE__; const char *filename = basename(__FILE__);. Alasan untuk penyalinan adalah bahwa nama samaran dapat mengubah string masukan. Anda juga harus waspada bahwa pointer hasil hanya baik sampai basenamedipanggil lagi. Ini berarti tidak aman untuk thread.
Steve Jessop
@ SeveJessop, ah saya lupa. Benar.
Prof. Falken
1
@Amigable: sejujurnya, saya menduga bahwa basenamepada kenyataannya tidak akan mengubah string input yang dihasilkan __FILE__, karena string input tidak memiliki /pada akhirnya dan jadi tidak perlu untuk modifikasi. Jadi Anda mungkin lolos begitu saja, tetapi saya pikir pertama kali seseorang melihat basename, mereka harus melihatnya dengan semua batasan.
Steve Jessop
@SteveJessop halaman manual BSd untuk basename () menyebutkan bahwa versi legen dari basename () mengambil karakter const * dan tidak memodifikasi string. Halaman manual linux tidak menyebutkan apapun tentang const tetapi menyebutkan bahwa ia dapat mengembalikan bagian dari string argumen. Jadi, lebih baik bersikap konservatif dengan basename ().
Prof. Falken
@SteveJessop, hehe, saya baru sekarang setelah melihat komentar Anda dengan hati-hati, setelah empat tahun, menyadari bahwa /pada akhir string berarti nama bas mungkin memiliki alasan yang baik untuk memodifikasi argumennya.
Prof. Falken
4
Jika Anda menggunakan CMAKEdengan kompiler GNU, globaldefinisi ini berfungsi dengan baik:
-Wno-builtin-macro-redefineduntuk menonaktifkan peringatan kompiler untuk mendefinisikan ulang __FILE__makro.
Untuk kompiler yang tidak mendukung ini, lihat cara Kuat di bawah ini.
Menghapus jalur proyek dari jalur file adalah persyaratan nyata Anda . Anda tidak akan suka membuang waktu untuk mencari tahu di mana header.hfile itu, src/foo/header.hatau src/bar/header.h.
Kita harus menghapus __FILE__makrocmake file konfigurasi.
Makro ini digunakan dalam sebagian besar kode yang ada. Cukup mendefinisikan kembali itu dapat membebaskan Anda.
Kompiler seperti gccmendefinisikan makro ini dari argumen baris perintah. Dan path lengkap ditulis dalam makefileyang dihasilkan oleh cmake.
CMAKE_*_FLAGSDiperlukan kode keras .
Ada beberapa perintah untuk menambahkan opsi atau definisi kompiler di beberapa versi yang lebih baru, seperti add_definitions() dan add_compile_definitions(). Perintah-perintah ini akan mengurai fungsi make seperti substsebelum diterapkan ke file sumber. Bukan itu yang kita inginkan.
Cara yang kuat untuk -Wno-builtin-macro-redefined .
Ini tidak berfungsi untuk konten yang berasal dari file yang disertakan.
chqrlie
dapatkah kamu memposting beberapa kode? Kasus umum adalah mengatur variabel dalam cakupan lokal dan menggunakannya dalam variabel lain.
Levi.G
Misalnya jika Anda menggunakan __FILE__dengan definisi Anda untuk menghasilkan diagnostik dalam fungsi sebaris yang didefinisikan dalam file header, diagnostik runtime akan melaporkan nama file yang diteruskan ke kompiler alih-alih nama file yang disertakan, sedangkan nomor baris akan lihat file yang disertakan.
chqrlie
ya, memang dirancang seperti itu, untuk penggunaan yang paling umum adalah #define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args). saat menggunakan LOG()makro, Anda tidak benar-benar ingin melihat log.hdi pesan. setelah semua, __FILE__makro diperluas di setiap file C / Cpp (unit kompilasi) alih-alih file yang disertakan.
Levi.G
3
Sedikit variasi tentang apa yang akan @ red1ynx usulkan untuk membuat makro berikut:
Saya melakukan makro __FILENAME__yang menghindari memotong jalur penuh setiap kali. Masalahnya adalah untuk menahan nama file yang dihasilkan dalam variabel cpp-local.
Ini dapat dengan mudah dilakukan dengan mendefinisikan variabel global statis dalam file .h . Definisi ini memberikan variabel yang terpisah dan independen di setiap file .cpp yang mencakup .h . Untuk menjadi multithreading-proof, patut untuk membuat variabel (s) juga thread local (TLS).
Satu variabel menyimpan Nama File (menyusut). Lain memegang nilai non-cut yang __FILE__memberi. File 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 () dapat diimplementasikan dengan cara yang berbeda, tetapi yang cepat dan sederhana sepertinya dengan strrchr:
push_macrodan pop_macrotidak standar. (gcc mendukung mereka untuk kompatibilitas dengan kompiler Microsoft Windows.) Dalam hal apa pun, tidak ada gunanya mendorong dan membuka definisi __FILE__; nilai yang dipulihkan tidak akan digunakan setelah akhir file sumber. Cara yang lebih bersih untuk mengubah nilainya __FILE__adalah#line __LINE__ "foobar.c"
Jika Anda berakhir di halaman ini mencari cara untuk menghapus jalur sumber absolut yang menunjuk untuk membangun lokasi yang jelek dari biner yang Anda kirim, di bawah ini mungkin sesuai dengan kebutuhan Anda.
Meskipun ini tidak menghasilkan jawaban yang tepat yang penulis ungkapkan keinginannya karena mengasumsikan penggunaan CMake , itu menjadi cukup dekat. Sayang sekali ini tidak disebutkan sebelumnya oleh siapa pun karena itu akan menyelamatkan saya banyak waktu.
OPTION(CMAKE_USE_RELATIVE_PATHS "If true, cmake will use relative paths" ON)
Pengaturan variabel di atas untuk ONakan menghasilkan perintah build dalam format:
cd /ugly/absolute/path/to/project/build/src &&
gcc <.. other flags ..>-c ../../src/path/to/source.c
Akibatnya, __FILE__makro akan memutuskan untuk../../src/path/to/source.c
Dokumentasi CMake 3.4+ menyatakan bahwa "Variabel ini tidak memiliki efek. Efek yang diimplementasikan sebagian pada rilis sebelumnya telah dihapus dalam CMake 3.4." Jika itu berhasil untuk Anda dengan 3,13, ada alasan lain untuk itu.
__BASE_FILE__Makro ini diperluas ke nama file input utama, dalam bentuk konstanta string C. Ini adalah file sumber yang ditentukan pada baris perintah preprocessor atau kompiler C
dan kemudian kontrol bagaimana Anda ingin menampilkan nama file dengan mengubah representasi file sumber (path lengkap / relatif path / nama file) pada waktu kompilasi.
tidak ada bedanya. Saya menggunakan __FILE__dan __BASE_FILE__bagaimanapun mereka berdua menunjukkan path lengkap ke file
mahmood
bagaimana Anda memanggil kompiler?
ziu
2
Lalu aku yakin SCONS memanggil gcc seperti ini gcc /absolute/path/to/file.c. Jika Anda menemukan cara untuk mengubah perilaku ini (membuka pertanyaan lain di SO, lol?), Anda tidak perlu memodifikasi string saat runtime
ziu
17
Jawaban ini 100% salah. __BASE_FILE__(seperti yang dikatakan dokumen, meskipun tidak jelas) menghasilkan nama file yang ditentukan pada baris perintah , misalnya test.catau /tmp/test.ctergantung pada bagaimana Anda memanggil kompiler. Itu persis sama dengan __FILE__, kecuali jika Anda berada di dalam file header, dalam hal ini __FILE__menghasilkan nama file saat ini (misalnya foo.h) sedangkan __BASE_FILE__terus menghasilkan test.c.
Quuxplusone
0
Berikut adalah solusi yang berfungsi untuk lingkungan yang tidak memiliki perpustakaan string (kernel Linux, sistem tertanam, dll):
Mungkin ingin memeriksa '/' dan '\', tergantung pada platform.
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__
dengan mengubah nama file yang Anda berikan pada baris perintah. Jadi alih-alihgcc /full/path/to/file.c
, cobacd /full/path/to; gcc file.c; cd -;
. Tentu saja ada sedikit lebih dari itu jika Anda mengandalkan direktori gcc saat ini untuk jalur sertakan atau lokasi file output. Sunting: gcc docs menyarankan bahwa ini adalah path lengkap, bukan argumen nama file input, tapi bukan itu yang saya lihat untuk gcc 4.5.3 di Cygwin. Jadi Anda bisa mencobanya di Linux dan lihat.__FILE__
dicetak sebagai../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.c
dan saya ingin tahu di mana gcc mengkompilasi file sehingga file ini relatif terletak seperti yang ditunjukkan.Jawaban:
Mencoba
Untuk Windows gunakan '\\' bukan '/'.
sumber
/
adalah pemisah jalur yang valid di Windows./
adalah pemisah jalur yang valid dalam nama file yang diteruskan ke CreateFile () dan sebagainya. Namun, itu tidak selalu berarti bahwa Anda dapat menggunakan/
sembarang di Windows karena ada tradisi (diwarisi dari CPM) menggunakan/
sebagai argumen memimpin pada prompt perintah. Tapi alat berkualitas akan berhati-hati untuk nama file perpecahan di kedua slash dan karakter backslash untuk menghindari masalah untuk orang-orang yang mengelola untuk menggunakan/
.char* fileName = basename(__FILE__);
pasti ada di Linux dan OS X, tidak tahu tentang Windows.strrchr("/" __FILE__, '/') + 1
. Dengan menambahkan "/" ke__FILE__
, strrchr dijamin untuk menemukan sesuatu, dan dengan demikian syarat:: tidak lagi diperlukan.Berikut tip jika Anda menggunakan cmake. Dari: http://public.kitware.com/pipermail/cmake/2013-January/053117.html
Saya menyalin tip sehingga semuanya ada di halaman ini:
Jika Anda menggunakan GNU make, saya tidak melihat alasan Anda tidak dapat memperluas ini ke makefile Anda sendiri. Misalnya, Anda mungkin memiliki garis seperti ini:
dimana
$(SOURCE_PREFIX)
awalan yang ingin Anda hapus.Kemudian gunakan
__FILENAME__
di tempat__FILE__
.sumber
__FILE__
bukan simbol preprocessor sederhana, itu berubah menjadi file saat ini sering digunakan untuk memancarkan nama file saat ini (header atau modul sumber). Ini__FILENAME__
hanya akan memiliki sumber terluar__FILE__
mana-mana keluar dari ruang kode.Saya baru saja memikirkan solusi hebat untuk ini yang bekerja dengan kedua sumber dan file header, sangat efisien dan bekerja pada waktu kompilasi di semua platform tanpa ekstensi spesifik kompiler. Solusi ini juga mempertahankan struktur direktori relatif dari proyek Anda, sehingga Anda tahu di folder mana file itu berada, dan hanya relatif terhadap akar proyek Anda.
Idenya adalah untuk mendapatkan ukuran direktori sumber dengan alat build Anda dan tambahkan saja ke
__FILE__
makro, menghapus direktori sepenuhnya dan hanya menampilkan nama file mulai dari direktori sumber Anda.Contoh berikut diimplementasikan menggunakan CMake, tetapi tidak ada alasan itu tidak akan bekerja dengan alat build lain, karena triknya sangat sederhana.
Pada file CMakeLists.txt, tentukan makro yang memiliki panjang jalur ke proyek Anda di CMake:
Pada kode sumber Anda, tentukan
__FILENAME__
makro yang hanya menambahkan ukuran jalur sumber ke__FILE__
makro:Kemudian gunakan makro baru ini bukan
__FILE__
makro. Ini berfungsi karena__FILE__
path akan selalu dimulai dengan path ke dir sumber CMake Anda. Dengan menghapusnya dari__FILE__
string, preprocessor akan menentukan nama file yang benar dan itu semua akan relatif terhadap akar proyek CMake Anda.Jika Anda peduli dengan kinerja, ini seefisien menggunakan
__FILE__
, karena keduanya__FILE__
danSOURCE_PATH_SIZE
dikenal sebagai konstanta waktu kompilasi, sehingga dapat dioptimalkan oleh kompiler.Satu-satunya tempat di mana ini akan gagal adalah jika Anda menggunakan ini pada file yang dihasilkan dan mereka berada di folder build off-source. Maka Anda mungkin harus membuat makro lain menggunakan
CMAKE_BUILD_DIR
variabel, bukanCMAKE_SOURCE_DIR
.sumber
__FILE__
adalah pointer ke array byte. Jadi menambahkan literal numerik hanyalah penambahan pointer. Sangat pintar @RenatoUtsch__FILE__
makro dan makro SOURCE_PATH_SIZE adalah konstanta diketahui pada waktu kompilasi. Saya berharap kompiler optimisasi modern dapat mendeteksi bahwa bagian dari string tidak digunakan dan hanya menghapusnya dari biner. Bagaimanapun, saya tidak berpikir beberapa byte ini akan membuat perbedaan yang signifikan dalam ukuran biner, jadi saya tidak akan terlalu peduli tentang itu.Solusi waktu murni kompilasi di sini. Ini didasarkan pada fakta bahwa
sizeof()
string literal mengembalikan panjangnya + 1.Jangan ragu untuk memperluas kaskade operator bersyarat ke nama file maksimum yang masuk akal dalam proyek. Panjang jalur tidak masalah, selama Anda memeriksa cukup jauh dari ujung string.
Saya akan melihat apakah saya bisa mendapatkan makro yang sama tanpa panjang kode keras dengan rekursi makro ...
sumber
-O1
untuk menggunakan ini agar waktu kompilasi.sizeof(s) > 2
cek terlebih dahulu. Juga, ini tidak bekerja pada waktu kompilasi untuk saya, di -Os. String path lengkap hadir dalam biner output.Paling tidak untuk gcc, nilai
__FILE__
path file seperti yang ditentukan pada baris perintah kompiler . Jika Anda mengompilasifile.c
seperti ini:yang
__FILE__
akan memperluas ke"/full/path/to/file.c"
. Jika Anda melakukan ini:kemudian
__FILE__
akan berkembang menjadi adil"file.c"
.Ini mungkin atau mungkin tidak praktis.
Standar C tidak memerlukan perilaku ini. Yang dikatakannya
__FILE__
hanyalah memperluas ke "Nama yang diduga dari fi le sumber saat ini (karakter string literal)".Alternatifnya adalah dengan menggunakan
#line
arahan. Itu menimpa nomor baris saat ini, dan opsional nama file sumber. Jika Anda ingin mengganti nama file tetapi meninggalkan nomor baris sendiri, gunakan__LINE__
makro.Misalnya, Anda dapat menambahkan ini di dekat bagian atas
file.c
:Satu-satunya masalah dengan ini adalah bahwa ia memberikan nomor baris yang ditentukan ke baris berikut , dan argumen pertama
#line
harus berupa urutan digit sehingga Anda tidak dapat melakukan sesuatu sepertiMemastikan bahwa nama file dalam
#line
arahan cocok dengan nama sebenarnya file tersebut dibiarkan sebagai latihan.Setidaknya untuk gcc, ini juga akan memengaruhi nama file yang dilaporkan dalam pesan diagnostik.
sumber
__LINE__
nilai satu. Jadi__LINE__
ditulis sesuai garisx
dievaluasix-1
. Setidaknya dengan gcc 5.4.#line __LINE__-1, "file.c"
. Saya akan memperbarui jawaban saya.Agak terlambat ke pesta, tetapi untuk GCC lihat
-ffile-prefix-map=old=new
opsi:Jadi untuk build Jenkins saya akan saya tambahkan
-ffile-prefix-map=${WORKSPACE}/=/
, dan yang lain untuk menghapus awalan instal paket dev lokal.CATATAN Sayangnya
-ffile-prefix-map
opsi ini hanya tersedia dalam GCC 8 dan seterusnya, seperti-fmacro-prefix-map
yang, saya pikir , melakukan__FILE__
bagiannya. Karena, katakanlah, GCC 5, kami hanya memiliki-fdebug-prefix-map
yang tidak (tampaknya) memengaruhi__FILE__
.sumber
-ffile-prefix-map
memang menyiratkan keduanya-fdebug-prefix-map
dan-fmacro-prefix-map
opsi. Lihat juga referensi di reproducible-builds.org/docs/build-path Bug GCC yang melacak-fmacro-prefix-map
dan-ffile-prefix-map
adalah gcc.gnu.org/bugzilla/show_bug.cgi?id=70268msvc2015u3, gcc5.4, clang3.8.0
'
Kode menghasilkan offset waktu kompilasi ketika:
gcc
: setidaknya gcc6.1 +-O1
msvc
: masukkan hasil ke dalam variabel constexpr:clang
: tetap ada pada tidak menyusun evaluasi waktuAda trik untuk memaksa ketiga kompiler melakukan kompilasi evaluasi waktu bahkan dalam konfigurasi debug dengan optimasi yang dinonaktifkan:
https://godbolt.org/z/u6s8j3
sumber
Di VC, saat menggunakan
/FC
,__FILE__
memperluas ke jalur penuh, tanpa/FC
opsi__FILE__
memperluas nama file. ref: di sinisumber
Tidak ada waktu kompilasi cara untuk melakukan ini. Jelas Anda dapat melakukannya saat runtime menggunakan runtime C, seperti yang ditunjukkan beberapa jawaban lainnya, tetapi pada waktu kompilasi, ketika pre-procesor masuk, Anda kurang beruntung.
sumber
strrchr
jawabannya bisa masuk akal dihitung pada saat kompilasi, walaupun tentu saja masih belum oleh preprocessor. Saya tidak tahu apakah gcc benar-benar melakukannya, saya belum memeriksa, tapi saya cukup yakin bahwa itu menghitungstrlen
string literal pada waktu kompilasi.__FILE__
dipanggil secara implisit olehassert()
, kebocoran ini dapat terjadi tanpa tindakan nyata apa pun.__FILE__
juga dapat mengungkapkan hal-hal yang mungkin tidak diinginkan pelanggan, jadi gunakan__FILE__
di mana saja - apakah itu berisi pathname absolut penuh atau hanya nama dasarnya - memiliki masalah yang sama seperti yang Anda tunjukkan. Dalam situasi ini semua output perlu diteliti dan API khusus harus diperkenalkan untuk output kepada pelanggan. Sisa output harus dibuang ke / dev / NULL atau stdout dan stderr harus ditutup. :-)Gunakan fungsi basename () , atau, jika Anda menggunakan Windows, _splitpath () .
Coba juga
man 3 basename
di shell.sumber
char file_copy[] = __FILE__; const char *filename = basename(__FILE__);
. Alasan untuk penyalinan adalah bahwa nama samaran dapat mengubah string masukan. Anda juga harus waspada bahwa pointer hasil hanya baik sampaibasename
dipanggil lagi. Ini berarti tidak aman untuk thread.basename
pada kenyataannya tidak akan mengubah string input yang dihasilkan__FILE__
, karena string input tidak memiliki/
pada akhirnya dan jadi tidak perlu untuk modifikasi. Jadi Anda mungkin lolos begitu saja, tetapi saya pikir pertama kali seseorang melihatbasename
, mereka harus melihatnya dengan semua batasan./
pada akhir string berarti nama bas mungkin memiliki alasan yang baik untuk memodifikasi argumennya.Jika Anda menggunakan
CMAKE
dengan kompiler GNU,global
definisi ini berfungsi dengan baik:sumber
Saya telah menggunakan solusi yang sama dengan jawaban @Patrick selama bertahun-tahun.
Ini memiliki masalah kecil ketika path lengkap berisi simbol-link.
Solusi yang lebih baik.
Kenapa harus menggunakan ini?
-Wno-builtin-macro-redefined
untuk menonaktifkan peringatan kompiler untuk mendefinisikan ulang__FILE__
makro.Menghapus jalur proyek dari jalur file adalah persyaratan nyata Anda . Anda tidak akan suka membuang waktu untuk mencari tahu di mana
header.h
file itu,src/foo/header.h
atausrc/bar/header.h
.Kita harus menghapus
__FILE__
makrocmake
file konfigurasi.Makro ini digunakan dalam sebagian besar kode yang ada. Cukup mendefinisikan kembali itu dapat membebaskan Anda.
Kompiler seperti
gcc
mendefinisikan makro ini dari argumen baris perintah. Dan path lengkap ditulis dalammakefile
yang dihasilkan olehcmake
.CMAKE_*_FLAGS
Diperlukan kode keras .Ada beberapa perintah untuk menambahkan opsi atau definisi kompiler di beberapa versi yang lebih baru, seperti
add_definitions()
danadd_compile_definitions()
. Perintah-perintah ini akan mengurai fungsi make sepertisubst
sebelum diterapkan ke file sumber. Bukan itu yang kita inginkan.Cara yang kuat untuk
-Wno-builtin-macro-redefined
.sumber
__FILE__
dengan definisi Anda untuk menghasilkan diagnostik dalam fungsi sebaris yang didefinisikan dalam file header, diagnostik runtime akan melaporkan nama file yang diteruskan ke kompiler alih-alih nama file yang disertakan, sedangkan nomor baris akan lihat file yang disertakan.#define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args)
. saat menggunakanLOG()
makro, Anda tidak benar-benar ingin melihatlog.h
di pesan. setelah semua,__FILE__
makro diperluas di setiap file C / Cpp (unit kompilasi) alih-alih file yang disertakan.Sedikit variasi tentang apa yang akan @ red1ynx usulkan untuk membuat makro berikut:
Di setiap file .c (pp) Anda tambahkan:
Maka Anda bisa merujuk
THIS_FILE_NAME
bukannya__FILE__
:Ini berarti pembangunan dilakukan sekali per file .c (pp) alih-alih setiap kali makro direferensikan.
Ini terbatas hanya untuk digunakan dari file .c (pp) dan tidak dapat digunakan dari file header.
sumber
Saya melakukan makro
__FILENAME__
yang menghindari memotong jalur penuh setiap kali. Masalahnya adalah untuk menahan nama file yang dihasilkan dalam variabel cpp-local.Ini dapat dengan mudah dilakukan dengan mendefinisikan variabel global statis dalam file .h . Definisi ini memberikan variabel yang terpisah dan independen di setiap file .cpp yang mencakup .h . Untuk menjadi multithreading-proof, patut untuk membuat variabel (s) juga thread local (TLS).
Satu variabel menyimpan Nama File (menyusut). Lain memegang nilai non-cut yang
__FILE__
memberi. File h:Makro itu sendiri memanggil metode dengan semua logika:
Dan fungsi diimplementasikan dengan cara ini:
RemovePath () dapat diimplementasikan dengan cara yang berbeda, tetapi yang cepat dan sederhana sepertinya dengan strrchr:
sumber
Compiler dentang terbaru memiliki
__FILE_NAME__
makro (lihat di sini ).sumber
hanya berharap untuk sedikit meningkatkan FILE makro:
#define FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
ini menangkap / dan \, seperti yang diminta Czarek Tomczak, dan ini bekerja sangat baik di lingkungan campuran saya.
sumber
FILE
adalah ide yang sangat buruk jika Anda memasukkannya<stdio.h>
.Mencoba
setelah menyertakan pernyataan dalam file sumber Anda dan tambahkan
di akhir file sumber Anda.
sumber
push_macro
danpop_macro
tidak standar. (gcc mendukung mereka untuk kompatibilitas dengan kompiler Microsoft Windows.) Dalam hal apa pun, tidak ada gunanya mendorong dan membuka definisi__FILE__
; nilai yang dipulihkan tidak akan digunakan setelah akhir file sumber. Cara yang lebih bersih untuk mengubah nilainya__FILE__
adalah#line __LINE__ "foobar.c"
Berikut adalah fungsi portabel yang berfungsi untuk Linux (jalur '/') dan Windows (campuran '\' dan '/').
Kompilasi dengan gcc, dentang dan vs.
Output standar:
sumber
Berikut adalah solusi yang menggunakan perhitungan waktu kompilasi:
sumber
Jika Anda berakhir di halaman ini mencari cara untuk menghapus jalur sumber absolut yang menunjuk untuk membangun lokasi yang jelek dari biner yang Anda kirim, di bawah ini mungkin sesuai dengan kebutuhan Anda.
Meskipun ini tidak menghasilkan jawaban yang tepat yang penulis ungkapkan keinginannya karena mengasumsikan penggunaan CMake , itu menjadi cukup dekat. Sayang sekali ini tidak disebutkan sebelumnya oleh siapa pun karena itu akan menyelamatkan saya banyak waktu.
Pengaturan variabel di atas untuk
ON
akan menghasilkan perintah build dalam format:Akibatnya,
__FILE__
makro akan memutuskan untuk../../src/path/to/source.c
Dokumentasi CMake
Waspadalah terhadap peringatan pada halaman dokumentasi:
Ini tidak dijamin untuk bekerja dalam semua kasus, tetapi bekerja di tambang - CMake 3.13 + gcc 4.5
sumber
Karena Anda menggunakan GCC, Anda dapat mengambil keuntungan dari
dan kemudian kontrol bagaimana Anda ingin menampilkan nama file dengan mengubah representasi file sumber (path lengkap / relatif path / nama file) pada waktu kompilasi.
sumber
__FILE__
dan__BASE_FILE__
bagaimanapun mereka berdua menunjukkan path lengkap ke filegcc /absolute/path/to/file.c
. Jika Anda menemukan cara untuk mengubah perilaku ini (membuka pertanyaan lain di SO, lol?), Anda tidak perlu memodifikasi string saat runtime__BASE_FILE__
(seperti yang dikatakan dokumen, meskipun tidak jelas) menghasilkan nama file yang ditentukan pada baris perintah , misalnyatest.c
atau/tmp/test.c
tergantung pada bagaimana Anda memanggil kompiler. Itu persis sama dengan__FILE__
, kecuali jika Anda berada di dalam file header, dalam hal ini__FILE__
menghasilkan nama file saat ini (misalnyafoo.h
) sedangkan__BASE_FILE__
terus menghasilkantest.c
.Berikut adalah solusi yang berfungsi untuk lingkungan yang tidak memiliki perpustakaan string (kernel Linux, sistem tertanam, dll):
Sekarang gunakan saja
FILENAME
alih-alih__FILENAME__
. Ya, ini masih runtime tetapi berfungsi.sumber
sumber
Jawaban singkat dan berfungsi untuk Windows dan * nix:
sumber
std::max<const char*>
bukan hanyastd::max
?