__FILE__ makro menunjukkan path lengkap


164

Makro standar yang __FILE__tersedia di C menunjukkan path lengkap ke file. Apakah ada cara untuk mempersingkat jalan? Maksudku, bukannya

/full/path/to/file.c

saya melihat

to/file.c

atau

file.c

18
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.
Prof. Falken

Jawaban:


166

Mencoba

#include <string.h>

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

Untuk Windows gunakan '\\' bukan '/'.


13
/adalah pemisah jalur yang valid di Windows.
Hans Passant

11
/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.
ɲeuroburɳ

54

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:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILENAME__='\"$(subst
  ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")

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:

CXX_FLAGS+=-D__FILENAME__='\"$(subst $(SOURCE_PREFIX)/,,$(abspath $<))\"'"

dimana $(SOURCE_PREFIX) awalan yang ingin Anda hapus.

Kemudian gunakan __FILENAME__di tempat __FILE__.


11
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:

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)

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.


4
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.

#define STRIPPATH(s)\
    (sizeof(s) > 2 && (s)[sizeof(s)-2] == '/' ? (s) + sizeof(s) - 1 : \
    sizeof(s) > 3 && (s)[sizeof(s)-3] == '/' ? (s) + sizeof(s) - 2 : \
    sizeof(s) > 4 && (s)[sizeof(s)-4] == '/' ? (s) + sizeof(s) - 3 : \
    sizeof(s) > 5 && (s)[sizeof(s)-5] == '/' ? (s) + sizeof(s) - 4 : \
    sizeof(s) > 6 && (s)[sizeof(s)-6] == '/' ? (s) + sizeof(s) - 5 : \
    sizeof(s) > 7 && (s)[sizeof(s)-7] == '/' ? (s) + sizeof(s) - 6 : \
    sizeof(s) > 8 && (s)[sizeof(s)-8] == '/' ? (s) + sizeof(s) - 7 : \
    sizeof(s) > 9 && (s)[sizeof(s)-9] == '/' ? (s) + sizeof(s) - 8 : \
    sizeof(s) > 10 && (s)[sizeof(s)-10] == '/' ? (s) + sizeof(s) - 9 : \
    sizeof(s) > 11 && (s)[sizeof(s)-11] == '/' ? (s) + sizeof(s) - 10 : (s))

#define __JUSTFILE__ STRIPPATH(__FILE__)

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 ...


3
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.


1
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__.


1
Opsi -ffile-prefix-mapmemang menyiratkan keduanya -fdebug-prefix-mapdan -fmacro-prefix-mapopsi. Lihat juga referensi di reproducible-builds.org/docs/build-path Bug GCC yang melacak -fmacro-prefix-mapdan -ffile-prefix-mapadalah gcc.gnu.org/bugzilla/show_bug.cgi?id=70268
Lekensteyn

6
  • C ++ 11
  • msvc2015u3, gcc5.4, clang3.8.0

    template <typename T, size_t S>
    inline constexpr size_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>
    inline constexpr size_t get_file_name_offset(T (& str)[1])
    {
        return 0;
    }

    '

    int main()
    {
         printf("%s\n", &__FILE__[get_file_name_offset(__FILE__)]);
    }

Kode menghasilkan offset waktu kompilasi ketika:

  • gcc: setidaknya gcc6.1 + -O1
  • msvc: masukkan hasil ke dalam variabel constexpr:

      constexpr auto file = &__FILE__[get_file_name_offset(__FILE__)];
      printf("%s\n", 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
        {
            static constexpr const 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__))]);
    }

https://godbolt.org/z/u6s8j3


Astaga, itu indah dan berhasil, terima kasih! Tidak tahu mengapa jawaban ini sangat diremehkan.
Roman

5

Di VC, saat menggunakan /FC, __FILE__memperluas ke jalur penuh, tanpa /FCopsi __FILE__memperluas nama file. ref: di sini


4

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.


1
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. :-)
tchen

4

Gunakan fungsi basename () , atau, jika Anda menggunakan Windows, _splitpath () .

#include <libgen.h>

#define PRINTFILE() { char buf[] = __FILE__; printf("Filename:  %s\n", basename(buf)); }

Coba juga man 3 basenamedi shell.


2
@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:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__='\"$(notdir $(abspath $<))\"'")

4

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.

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")

Kenapa harus menggunakan ini?

  • -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 .

include(CheckCCompilerFlag)
check_c_compiler_flag(-Wno-builtin-macro-redefined SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
if (SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined")
endif (SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-Wno-builtin-macro-redefined SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)
if (SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined")
endif (SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)

Ingatlah untuk menghapus opsi kompiler ini dari set(*_FLAGS ... -D__FILE__=...)baris.


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:

#define SET_THIS_FILE_NAME() \
    static const char* const THIS_FILE_NAME = \
        strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__;

Di setiap file .c (pp) Anda tambahkan:

SET_THIS_FILE_NAME();

Maka Anda bisa merujuk THIS_FILE_NAMEbukannya __FILE__:

printf("%s\n", THIS_FILE_NAME);

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.


3

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:

static __declspec( thread ) const char* fileAndThreadLocal_strFilePath = NULL;
static __declspec( thread ) const char* fileAndThreadLocal_strFileName = NULL;

Makro itu sendiri memanggil metode dengan semua logika:

#define __FILENAME__ \
    GetSourceFileName(__FILE__, fileAndThreadLocal_strFilePath, fileAndThreadLocal_strFileName)

Dan fungsi diimplementasikan dengan cara ini:

const char* GetSourceFileName(const char* strFilePath, 
                              const char*& rstrFilePathHolder, 
                              const char*& 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:

const char* removePath(const char* path)
{
    const char* pDelimeter = strrchr (path, '\\');
    if (pDelimeter)
        path = pDelimeter+1;

    pDelimeter = strrchr (path, '/');
    if (pDelimeter)
        path = pDelimeter+1;

    return path;
}


2

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.


6
Mendefinisikan nama makro FILEadalah ide yang sangat buruk jika Anda memasukkannya <stdio.h>.
Keith Thompson

senang mendengarnya. Saya hanya ingin menunjukkan Czarek pada solusi saya, jadi saya tidak peduli dengan skema penamaan.
alexander golks

2

Mencoba

#pragma push_macro("__FILE__")
#define __FILE__ "foobar.c"

setelah menyertakan pernyataan dalam file sumber Anda dan tambahkan

#pragma pop_macro("__FILE__")

di akhir file sumber Anda.


2
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"
Keith Thompson

1
Dan ini menyebabkan kesalahan internal pada preprosesor gcc. gcc.gnu.org/bugzilla/show_bug.cgi?id=69665
Keith Thompson

1

Berikut adalah fungsi portabel yang berfungsi untuk Linux (jalur '/') dan Windows (campuran '\' dan '/').
Kompilasi dengan gcc, dentang dan vs.

#include <string.h>
#include <stdio.h>

const char* GetFileName(const char *path)
{
    const char *name = NULL, *tmp = NULL;
    if (path && *path) {
        name = strrchr(path, '/');
        tmp = strrchr(path, '\\');
        if (tmp) {
             return name && name > tmp ? name + 1 : tmp + 1;
        }
    }
    return name ? name + 1 : path;
}

int main() {
    const char *name = NULL, *path = NULL;

    path = __FILE__;
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path ="/tmp/device.log";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:\\Downloads\\crisis.avi";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:\\Downloads/nda.pdf";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:/Downloads\\word.doc";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = NULL;
    name = GetFileName(NULL);
    printf("path: %s, filename: %s\n", path, name);

    path = "";
    name = GetFileName("");
    printf("path: %s, filename: %s\n", path, name);

    return 0;
}

Output standar:

path: test.c, filename: test.c
path: /tmp/device.log, filename: device.log
path: C:\Downloads\crisis.avi, filename: crisis.avi
path: C:\Downloads/nda.pdf, filename: nda.pdf
path: C:/Downloads\word.doc, filename: word.doc
path: (null), filename: (null)
path: , filename: 

1

Berikut adalah solusi yang menggunakan perhitungan waktu kompilasi:

constexpr auto* getFileName(const char* const path)
{
    const auto* startPosition = path;
    for (const auto* currentCharacter = path;*currentCharacter != '\0'; ++currentCharacter)
    {
        if (*currentCharacter == '\\' || *currentCharacter == '/')
        {
            startPosition = currentCharacter;
        }
    }

    if (startPosition != path)
    {
        ++startPosition;
    }

    return startPosition;
}

std::cout << getFileName(__FILE__);

1

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

Waspadalah terhadap peringatan pada halaman dokumentasi:

Gunakan jalur relatif (Mungkin tidak berfungsi!).

Ini tidak dijamin untuk bekerja dalam semua kasus, tetapi bekerja di tambang - CMake 3.13 + gcc 4.5


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.
mike.dld

0

Karena Anda menggunakan GCC, Anda dapat mengambil keuntungan dari

__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.


6
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):

#define FILENAME ({ \
    const char* filename_start = __FILE__; \
    const char* filename = filename_start; \
    while(*filename != '\0') \
        filename++; \
    while((filename != filename_start) && (*(filename - 1) != '/')) \
        filename--; \
    filename; })

Sekarang gunakan saja FILENAMEalih-alih __FILENAME__. Ya, ini masih runtime tetapi berfungsi.


Mungkin ingin memeriksa '/' dan '\', tergantung pada platform.
Technophile

0
#include <algorithm>
#include <string>
using namespace 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

0

Jawaban singkat dan berfungsi untuk Windows dan * nix:

#define __FILENAME__ std::max<const char*>(__FILE__,\
    std::max(strrchr(__FILE__, '\\')+1, strrchr(__FILE__, '/')+1))

Mengapa Anda perlu, std::max<const char*>bukan hanya std::max?
chqrlie
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.