__FILE__ makrosu tam yolu gösterir


164

__FILE__C'de bulunan standart önceden tanımlanmış makro , dosyanın tam yolunu gösterir. Yolu kısaltmanın bir yolu var mı? Demek istediğim

/full/path/to/file.c

anlıyorum

to/file.c

veya

file.c

18
Yalnızca önişlemci için bir çözüm bulmak gerçekten harika olurdu. Korkarım dize işlemlerine dayalı öneriler zamanında yürütülür.
cdleonard

9
Gcc kullandığınızdan __FILE__, komut satırında ilettiğiniz dosya adını değiştirerek neleri içerdiğini değiştirebileceğinizi düşünüyorum . Bu yüzden yerine gcc /full/path/to/file.cdeneyin cd /full/path/to; gcc file.c; cd -;. Tabii ki, ekleme yolu veya çıktı dosyası konumu için gcc'nin geçerli dizinine güveniyorsanız, bundan biraz daha fazlası var. Düzenleme: gcc dokümanlar , girdi dosya adı bağımsız değişkeni değil , tam yol olduğunu önerir , ancak Cygwin'de gcc 4.5.3 için gördüğüm şey bu değildir. Yani Linux'ta da deneyebilir ve görebilirsiniz.
Steve Jessop

4
GCC 4.5.1 (özellikle arm-none-eabi için oluşturulmuştur) komut satırında dosya adının tam metnini kullanır. Benim durumumda, IDE'nin geçerli dizini mantıklı bir yere (belki de proje dosyasının konumu) koymak veya yapılandırılabilir ve oradan göreceli yollar kullanmak yerine GCC'yi tüm dosya adlarıyla tam olarak nitelendirmek hatasıydı. Birçok IDE'lerin (özellikle Windows'da) "şu anki" dizininin gerçekten bir GUI uygulaması için nerede olduğunu açıklamakla ilgili bir çeşit rahatsızlıktan şüphelendiğini düşünüyorum.
RBerteig

3
@SteveJessop - Umarım bu yorumu okursunuz. Ben __FILE__basıldığı gibi gördüğüm bir durum var ../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.cve nerede gcc dosya görüldüğü gibi nispeten bulunduğu şekilde nerede derledi bilmek istiyorum.
Chan Kim

1
Bu soru bağlantılı olanın bir kopyası değildir. Birincisi, bağlı olan C ++ ile ilgilidir ve cevaplar sonuç olarak C ++ makro ezoteriklerine girer. İkincisi, OP'nin sorusunda makro bir çözümü zorunlu kılan hiçbir şey yok. Sadece ciddi bir soruna işaret ediyor ve açık uçlu bir soru soruyor.
Prof. Falken

Yanıtlar:


166

Deneyin

#include <string.h>

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

Windows için '/' yerine '\\' kullanın.


13
/Windows'ta geçerli bir yol ayırıcıdır.
Hans Passant

11
/CreateFile () öğesine aktarılan dosya adlarında geçerli bir yol ayırıcıdır. Bununla birlikte, bu her zaman /Windows'ta herhangi bir yerde kullanabileceğiniz anlamına gelmez, çünkü /komut isteminde bağımsız değişken olarak kullanma geleneği (BGBM'den devralınmıştır) vardır . Ama kaliteli bir aracı olarak bölünmüş dosya adları için dikkatli olacağını hem kullanımı yönetirim millet için herhangi bir sorun önlemek için eğik çizgi ve ters eğik çizgi karakterleri /.
RBerteig

5
@AmigableClarkKant, hayır, her iki ayırıcıyı aynı dosya adında karıştıramazsınız.
RBerteig

2
Platformunuz destekliyorsa char* fileName = basename(__FILE__); Kesinlikle Linux ve OS X'te var, Windows hakkında bilmiyorum.
JeremyP

14
Bu kısaltılabilir strrchr("/" __FILE__, '/') + 1. "/" İşaretini ekleyerek, strrchr'nin bir __FILE__şey bulması garanti edilir ve bu nedenle koşullu ?: Artık gerekli değildir.
Euroburɳ

54

İşte cmake kullanıyorsanız bir ipucu. Gönderen: http://public.kitware.com/pipermail/cmake/2013-Ocak/053117.html

Bahşişi kopyalarım bu yüzden hepsi bu sayfada

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

GNU marka kullanıyorsanız, bunu kendi markalarınıza genişletemezsiniz. Örneğin, böyle bir satırınız olabilir:

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

$(SOURCE_PREFIX)kaldırmak istediğiniz önek nerede .

Sonra kullanmak __FILENAME__yerine __FILE__.


11
Korkarım o zaman bu başlık dosyasında başvurulan DOSYA için çalışmaz .
Baiyan Huang

2
@BaiyanHuang ile aynı fikirde ama yorumun açık olduğundan emin değilim. __FILE__basit bir önişlemci sembolü değildir, geçerli dosyaya yapılan değişiklikler genellikle geçerli dosyanın adını (başlık veya kaynak modülü) yaymak için kullanılır. Bu __FILENAME__sadece en dıştaki kaynağa sahip olacaktır
19'da

3
Bu yanıtın çözümü Bourne kabuğu kaçışlarını kullandığından taşınabilir değildir. Temiz ve taşınabilir bir şekilde uygulamak için CMake'i kullanmak daha iyidir. Bkz makro cevabı define_file_basename_for_sources burada.
Colin D Bennett

3
GNU Make varyantı CFLAGS + = -D__FILENAME __ = \ "$ (notdir $ <) \"
ctuffli

4
@firegurafiku Gömülü platformlarda, kod boyutu genellikle hızdan daha kısıtlıdır. Her dosyada hata ayıklama ifadeleri varsa, bu, dosya boyutunu tam yol dizeleriyle hızla balonlayabilir. Şu anda böyle bir platformla uğraşıyorum ve __FILE__her yere atıfta bulunmak kod alanından esiyor.
mrtumnus

26

Sadece kaynak ve başlık dosyaları ile çalışan, çok verimli ve derleyiciye özgü uzantıları olmayan tüm platformlarda derleme zamanında çalışan harika bir çözüm düşündüm. Bu çözüm aynı zamanda projenizin göreli dizin yapısını da korur, böylece dosyanın hangi klasörde olduğunu ve yalnızca projenizin kök dizinine göre olduğunu bilirsiniz.

Fikir, oluşturma aracınızla kaynak dizinin boyutunu almak ve __FILE__makroya eklemek , dizini tamamen kaldırmak ve yalnızca kaynak dizininizden başlayan dosya adını göstermektir.

Aşağıdaki örnek CMake kullanılarak uygulanır, ancak başka bir oluşturma aracıyla çalışmamasının bir nedeni yoktur, çünkü hile çok basittir.

CMakeLists.txt dosyasında, CMake'teki projenize giden yolun uzunluğuna sahip bir makro tanımlayın:

# 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}")

Kaynak kodunuzda, __FILENAME__yalnızca makroya kaynak yolu boyutunu ekleyen bir makro tanımlayın __FILE__:

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)

Ardından, makro yerine bu yeni makroyu kullanın __FILE__. Bu, __FILE__yol her zaman CMake kaynak dizininizin yolu ile başlayacağı için çalışır . __FILE__Ön işlemcinin dizeden kaldırılmasıyla , doğru dosya adını belirtmeye özen gösterecek ve tümü CMake projenizin köküne göreli olacaktır.

Performansı önemsiyorsanız, bu kullanım kadar verimlidir __FILE__, çünkü hem __FILE__ve hem de SOURCE_PATH_SIZEderleme zamanı sabitleri bilinir, böylece derleyici tarafından optimize edilebilir.

Bunun başarısız olacağı tek yer, bunu oluşturulan dosyalarda kullanıyorsanız ve kaynak dışı bir derleme klasöründeyseniz. Ardından, CMAKE_BUILD_DIRdeğişkeni kullanmak yerine muhtemelen başka bir makro oluşturmanız gerekir CMAKE_SOURCE_DIR.


4
İlk başta bunu anlamadım. Örnekleri kodladım ve gcc ve clang'a karşı çalıştırdım ve çalışıyorlar. Ben de sadece çeşitli değişmez sayısal değerleri ekleyerek deney ve bu beklediğim gibi davranır. Sonra nihayet üzerime geldi. __FILE__bir bayt dizisinin göstergesidir. Dolayısıyla, sayısal bir değişmez eklemek yalnızca işaretçi eklemesidir. @RenatoUtsch
Daniel

Yalnızca bir kaynak dosya cmake list dizini altındaysa çalışır. Bir kaynak dosya dışarıda ise, o zaman bozulur, değişmez bir dize dışında erişime sahip olabilir. Bu yüzden dikkatli olun.
Andry

Aslında kod boyutunu azaltmaz. Sanırım bütün yol hala ikili dosyaya derlenmiş, sadece işaretçi değiştirilmiş.
Tarion

Hem __FILE__makro, hem de SOURCE_PATH_SIZE makroları derleme zamanında bilinen sabitlerdir. Modern optimizasyon derleyicilerinin dizenin bir kısmının kullanılmadığını algılayabilmesini ve sadece ikili dosyadan çıkarmasını beklerdim. Her neyse, bu birkaç baytın ikili boyutta önemli bir fark yaratacağını sanmıyorum, bu yüzden gerçekten umursamıyorum.
RenatoUtsch

@RenatoUtsch Üzerinde çalıştığım projede sadece dosya adını belirten bir değişiklik var, ancak başlığa C dosya adını vermenin dezavantajı var. Değişiklik tekrarlanabilir yapıları elde etmek için yapıldı. Peki -O2 ile gcc ile, dize gerçekten optimize edilecek ve yapı tekrarlanabilir hale getirilecek mi?
Paul Stelian

18

Zaman çözümünü burada derleyin. sizeof()Bir dizgenin değişmezinin uzunluğunu + 1 döndürmesi gerçeğine dayanır .

#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__)

Koşullu operatör kaskadını projedeki maksimum mantıklı dosya adına genişletmekten çekinmeyin. Dizenin sonundan yeterince kontrol ettiğiniz sürece yol uzunluğu önemli değildir.

Makro özyineleme ile sabit kodlu uzunluğu olmayan benzer bir makro alıp alamayacağımı göreceğim ...


3
Güzel cevap. -O1Derleme zamanı olarak bunu kullanmak için en azından kullanmanız gerekir.
Dijital Travma

1
Bu geriye değil mi? En son '/' oluşumunu bulmak istiyorsunuz , yani önce sizeof(s) > 2çekle başlamalısınız . Ayrıca, bu benim için derleme zamanında işe yaramadı, -Os. Tam yol dizeleri çıkış ikili dosyasında mevcuttu.
mrtumnus

15

En azından gcc için değeri , derleyicinin komut satırında belirtilen__FILE__ dosya yoludur . Eğer böyle derlerseniz :file.c

gcc -c /full/path/to/file.c

__FILE__genişleyecektir "/full/path/to/file.c". Bunun yerine bunu yaparsanız:

cd /full/path/to
gcc -c file.c

sonra __FILE__sadece genişleyecektir "file.c".

Bu pratik olabilir veya olmayabilir.

C standardı bu davranışı gerektirmez. Tek söylediği şey __FILE__, "Geçerli kaynak dosyasının varsayılan adı (bir karakter dizesi değişmezi)" olarak genişlemesi.

Alternatif olarak #lineyönergeyi kullanmaktır . Geçerli satır numarasını ve isteğe bağlı olarak kaynak dosya adını geçersiz kılar. Dosya adını geçersiz kılmak, ancak satır numarasını yalnız bırakmak istiyorsanız, __LINE__makroyu kullanın .

Örneğin, bunu aşağıdakilerin üst kısmına ekleyebilirsiniz file.c:

#line __LINE__ "file.c"

Bununla ilgili tek sorun, belirtilen satır numarasını aşağıdaki satıra ataması ve ilk argümanın basamak dizisi#line olması gerektiğinden , böyle bir şey yapamazsınız.

#line (__LINE__-1) "file.c"  // This is invalid

Yönergedeki dosya adının dosyanın #linegerçek adıyla eşleştiğinden emin olmak alıştırma olarak bırakılır.

En azından gcc için bu, tanılama mesajlarında bildirilen dosya adını da etkiler.


1
Keith Thompson harika bir çözüm, teşekkürler. Tek sorun, bu makronun __LINE__değeri birer azalttığı görülüyor . Bu yüzden __LINE__satır olarak yazılmış xjest olarak değerlendirildi x-1. En azından gcc 5.4 ile.
elklepo

1
@Klepak: Haklısın ve bu standart davranış. Yönerge "uygulamanın aşağıdaki kaynak satırı sırası, basamak sırası tarafından belirtildiği gibi bir satır numarası olan bir kaynak satırı ile başlıyormuş gibi davranmasına neden olur". Ve bir basamak dizisi olmalı , bu yüzden kullanamazsınız #line __LINE__-1, "file.c". Cevabımı güncelleyeceğim.
Keith Thompson

1
Clang, MSVC veya Intel C Compiler gibi diğer derleyiciler için nasıl olurdu?
Franklin Yu

8

Partiye biraz geç, ancak GCC için -ffile-prefix-map=old=newseçeneğe bir göz atın :

Eski dizinde bulunan dosyaları derlerken , derleme sonucunda bunlara yapılan referansları, dosyalar yeni dizinde bulunuyormuş gibi kaydedin . Bu seçeneğin belirlenmesi, tüm ayrı -f*-prefix-mapseçeneklerin belirlenmesine eşdeğerdir . Bu, konumdan bağımsız yeniden üretilebilir yapılar yapmak için kullanılabilir. Ayrıca bkz . -fmacro-prefix-mapVe -fdebug-prefix-map.

Bu yüzden Jenkins yapılarıma ekleyeceğim -ffile-prefix-map=${WORKSPACE}/=/ve başka bir yerel dev paket yükleme önekini kaldıracağım.

NOT Maalesef -ffile-prefix-mapseçenek olarak, itibaren GCC 8'de sadece avalable olduğunu -fmacro-prefix-map, hangi bence , yaptığı __FILE__parçasını. Diyelim ki GCC 5, sadece -fdebug-prefix-mapetkilemeyen (görünmeyen) elimizde __FILE__.


1
Seçenek -ffile-prefix-mapgerçekten hem ima -fdebug-prefix-mapve -fmacro-prefix-mapseçenekleri. En başvuruları Ayrıca bkz reproducible-builds.org/docs/build-path izler olduğunu GCC hatası -fmacro-prefix-mapve -ffile-prefix-mapbir 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__)]);
    }

Kod aşağıdaki durumlarda bir derleme zamanı ofseti oluşturur:

  • gcc: en az gcc6.1 + -O1
  • msvc: sonucu constexpr değişkenine koy:

      constexpr auto file = &__FILE__[get_file_name_offset(__FILE__)];
      printf("%s\n", file);
  • clang: Zaman değerlendirmesini derlememeye devam ediyor

3 derleyicinin tümünü, hata ayıklama yapılandırmasında bile devre dışı bırakma optimizasyonu ile zaman değerlendirmesini derlemek için bir hile vardır:

    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


Dostum, bu çok güzel ve işe yarıyor, teşekkürler! Bu cevabın neden bu kadar önemsiz olduğunu bilmiyorum.
Roman

5

Kullanırken VC yılında /FC, __FILE__olmadan, tam yol genişler /FCseçeneği __FILE__dosya adını genişler. ref: burada


4

Bunu yapmanın derleme zamanı yok. Açıkçası, diğer cevapların bazılarının gösterdiği gibi, C çalışma zamanını kullanarak çalışma zamanında yapabilirsiniz, ancak derleme zamanında, ön-işlemcinin devreye girmesi, şansınız kalmaz.


1
strrchrelbette hala önişlemci tarafından olmasa da , cevap derleme zamanında makul bir şekilde hesaplanabilir. Gcc aslında yapar mı bilmiyorum, kontrol etmedi, ama strlenderleme zamanında dize değişmezleri hesaplamak eminim .
Steve Jessop

@Steve - belki, ama bu derleyiciye özgü davranışlara büyük bir bağımlılık.
Sean

Bu kodun performans açısından kritik olduğundan şüphe duyduğum için büyük bir bağımlılık olduğunu sanmıyorum. Ve eğer öyleyse, döngüden çıkarın. Bunun büyük bir anlaşma olduğu durumlarda, kesinlikle yalnızca basename içeren bir dize değişmezine ihtiyacınız olduğu için , kaynak üzerinde bazı komut dosyası çalıştırarak derleme sırasında doğru dizeyi hesaplayabilirsiniz .
Steve Jessop

5
Performans açısından kritik olmayabilir, ancak gizlilik açısından kritik olarak kolayca görülebilir. Yayınlanmış bir EXE dosyasına dondurulmuş dizelerdeki müşteri başına kuruluş uygulamalarını ortaya çıkarmanın gerçek bir nedeni yok. Daha da kötüsü, bir müşteri adına oluşturulan uygulamalar için, bu dizeler müşterimin kendi ürününün yazarı olmaması gibi tercih etmeyeceği şeyleri ortaya çıkarabilir. Yana __FILE__örtülü tarafından çağrılır assert(), bu sızıntı başka aşikar eylemi olmaksızın gerçekleşebilir.
RBerteig

@RBerteig __FILE__kendisinin temel adı, müşterinin tercih etmeyeceği şeyleri de ortaya çıkarabilir, bu nedenle __FILE__- tam mutlak yol adını veya sadece taban adını içeriyor olsun - herhangi bir yerde kullanmak - belirttiğiniz aynı sorunlara sahiptir. Bu durumda, tüm çıktıların incelenmesi gerekir ve müşterilere çıktı için özel bir API kullanılmalıdır. Çıktının geri kalanı / dev / NULL veya stdout'a dökülmeli ve stderr kapatılmalıdır. :-)
tchen

4

Kullanım basename () Windows üzerinde ise, işlev ya, _splitpath () .

#include <libgen.h>

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

Ayrıca man 3 basenamebir kabukta deneyin .


2
@mahmood: char file_copy[] = __FILE__; const char *filename = basename(__FILE__);. Kopyalamanın nedeni, taban adının giriş dizesini değiştirebilmesidir. Ayrıca sonuç göstergesinin basenametekrar çağrılıncaya kadar iyi olduğunu da göz önünde bulundurmalısınız . Bu, iş parçacığı için güvenli olmadığı anlamına gelir.
Steve Jessop

@SteveJessop, ah unuttum. Doğru.
Prof. Falken

1
@Amigable: adil olmak gerekirse basename, aslında giriş dizesi sonunda bir yok ve bu nedenle değişiklik gerek yok __FILE__, çünkü sonuçta gelen giriş dizesini değiştirmek olmaz şüpheli /. Yani ondan kurtulabilirsiniz, ama birisinin ilk gördüğünde basename, tüm kısıtlamalarla görmeleri gerektiğini anlıyorum.
Steve Jessop

@SteveJessop basename () için BSd man sayfasını basename () 'in eski sürümünün bir cons * karakteri aldığını ve dizeyi değiştirmediğini belirtin. Linux man sayfası const hakkında hiçbir şeyden bahsetmez, ancak argüman dizesinin bir kısmını döndürebileceğinden bahseder. Yani, en iyisi basename () ile uğraşırken muhafazakar olun.
Prof. Falken

@SteveJessop, hehe, ben sadece şimdi dikkatle Yorumlarınız baktıktan sonra, dört yıl sonra, Fark o /dize araçlarının basename sonunda argüman değiştirmek için iyi bir neden olabilir.
Prof. Falken

4

CMAKEGNU derleyicisi ile kullanıyorsanız bu globaltanımlama düzgün çalışır:

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

4

Yıllardır @Patrick'in cevabı ile aynı çözümü kullanıyorum .

Tam yol sembol bağlantısı içerdiğinde küçük bir sorun vardır.

Daha iyi çözüm.

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 $<))\"'")

Bunu neden kullanmalıyım?

  • -Wno-builtin-macro-redefined__FILE__makrosu yeniden tanımlamak için derleyici uyarılarını sessize almak için .

    Bu derleyiciler bunu desteklemediğinden, aşağıdaki Sağlam yola bakın.

  • Proje yolunu dosya yolundan çıkarın gerçek gereksiniminizdir. Bir header.hdosyanın nerede olduğunu bulmak için zaman kaybetmek istemezsiniz src/foo/header.hveya src/bar/header.h.

  • Biz şerit gerektiğini __FILE__de makro cmakeyapılandırma dosyası.

    Bu makro, mevcut kodların çoğunda kullanılır. Sadece yeniden tanımlamak sizi serbest bırakabilir.

    Like derleyicileri gccbu makroyu komut satırı argümanlarından önceden tanımlar. Ve tam yol makefiletarafından üretilen s yazılır cmake.

  • Sabit kod girilmesi CMAKE_*_FLAGSgerekiyor.

    Bazı son sürümlerde derleyici seçeneklerini veya tanımlarını eklemek için bazı komutlar vardır, add_definitions()ve gibi add_compile_definitions(). Bu komutlar, substkaynak dosyalara uygulanmadan önce make işlevlerini ayrıştırır . Biz istemiyoruz.

Sağlam bir yol -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)

Bu derleyici seçeneğini set(*_FLAGS ... -D__FILE__=...)satırdan kaldırmayı unutmayın .


Bu, içerme dosyalarından gelen içerik için çalışmaz.
chqrlie

biraz kod gönderebilir misin Yaygın bir durum, değişkenleri yerel kapsamda ayarlamak ve başka bir kapsamda kullanmaktır.
Levi.G

Örneğin __FILE__, bir başlık dosyasında tanımlanan satır içi işlevde tanı oluşturmak için tanımınızla birlikte kullanırsanız , çalışma zamanı tanılaması, dahil etme dosyasının adı yerine derleyiciye iletilen dosyanın adını bildirir, satır numarası içerme dosyasına bakın.
chqrlie

evet, öyle tasarlanmış, en yaygın kullanım içindir #define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args). LOG()makroyu kullanırken, log.hiletilerde gerçekten görmek istemezsiniz . sonuçta, __FILE__makro dahil edilen dosyalar yerine her C / Cpp dosyasında (derleme birimi) genişletilir.
Levi.G

3

@ Red1ynx'in önerdiği şeyin küçük bir varyasyonu aşağıdaki makroyu oluşturacaktır:

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

.C (pp) dosyalarınızın her birine şunları ekleyin:

SET_THIS_FILE_NAME();

Sonra THIS_FILE_NAMEbunun yerine başvurabilirsiniz __FILE__:

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

Bu, yapıya, makroya her başvuruda bulunmak yerine .c (pp) dosyası başına bir kez gerçekleştirildiği anlamına gelir.

Yalnızca .c (pp) dosyalarından kullanılmak üzere sınırlıdır ve başlık dosyalarından kullanılamaz.


3

__FILENAME__Her seferinde tam yol kesmekten kaçınan bir makro yaptım . Sorun, ortaya çıkan dosya adını bir cpp-local değişkeninde tutmaktır.

.H dosyasında statik bir global değişken tanımlayarak kolayca yapılabilir . Bu tanım, her ayrı ve bağımsız değişkenler verir .Cpp içeren dosyanın .h . Çok iş parçacıklı bir kanıt olmak için değişken (ler) i yerel olarak da (TLS) yapmaya değer.

Bir değişken Dosya Adını (küçültülmüş) saklar. Bir diğeri, verilen kesilmemiş değeri tutar __FILE__. H dosyası:

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

Makronun kendisi tüm mantıkla yöntemi çağırır:

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

Ve işlev şu şekilde uygulanır:

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 () farklı şekillerde uygulanabilir, ancak hızlı ve basit, strrchr ile birlikte görünür:

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

sadece DOSYA makrosunu biraz geliştirmeyi umuyoruz:

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

bu Czarek Tomczak'ın istediği gibi yakalar ve / ve karışık ortamımda harika çalışır.


6
FILEEklediğiniz bir makro tanımlamak gerçekten kötü bir fikirdir <stdio.h>.
Keith Thompson

bunu bildiğim iyi oldu. Sadece Czarek \\ / çözüm göstermek istedim, bu yüzden adlandırma düzenleri ile rahatsız etmeyin.
Alexander Golks

2

Deneyin

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

kaynak dosyanıza ifadeleri ekledikten sonra

#pragma pop_macro("__FILE__")

kaynak dosyanızın sonunda.


2
push_macrove pop_macrostandart değildir. (gcc bunları Microsoft Windows derleyicileriyle uyumluluk için destekler.) Her durumda, tanımını zorlamanın ve patlatmanın bir anlamı yoktur __FILE__; geri yüklenen değer yine de kaynak dosyanın bitiminden sonra kullanılmaz. Değerini değiştirmek için bir temizleyici yolu __FILE__olduğunu#line __LINE__ "foobar.c"
Keith Thompson

1
Bu, gcc'nin ön işlemcisinde dahili bir hataya neden olur. gcc.gnu.org/bugzilla/show_bug.cgi?id=69665
Keith Thompson

1

Hem Linux (yol '/') hem de Windows ('\' ve '/' karışımı) için çalışan taşınabilir bir işlev.
Gcc, clang ve vs. ile derler

#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;
}

Standart çıktı:

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

Derleme zamanı hesaplamasını kullanan çözüm:

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

Bu sayfada, gönderim yaptığınız ikili dosyadan çirkin bir yapı konumuna işaret eden mutlak kaynak yolunu kaldırmanın bir yolunu bulduysanız, aşağıda ihtiyaçlarınıza uygun olabilir.

Bu, yazarın CMake'in kullanımını üstlendiği için arzusunu dile getirdiği yanıtı tam olarak vermese de, oldukça yakınlaşıyor. Bana daha fazla zaman kazandıracak gibi bu daha önce kimse tarafından belirtilmemiş yazık.

OPTION(CMAKE_USE_RELATIVE_PATHS "If true, cmake will use relative paths" ON)

Değişkenin yukarısına ayarlanması ONbiçiminde build komutu oluşturur:

cd /ugly/absolute/path/to/project/build/src && 
    gcc <.. other flags ..> -c ../../src/path/to/source.c

Sonuç olarak, __FILE__makro../../src/path/to/source.c

CMake belgeleri

Yine de belgeler sayfasındaki uyarılara dikkat edin:

Göreli yollar kullanın (Çalışmayabilir!).

Her durumda çalışacağı garanti edilmez, ancak madende çalıştı - CMake 3.13 + gcc 4.5


CMake 3.4+ belgeleri, "Bu değişkenin etkisi yoktur. Önceki sürümlerde kısmen uygulanan efekt CMake 3.4'te kaldırılmıştır." 3.13 ile işe yaradıysa, bunun başka bir nedeni var.
mike.dld

0

GCC kullandığınız için ,

__BASE_FILE__Bu makro, ana girdi dosyasının adına, C dizesi sabiti biçiminde genişler. Bu, önişlemcinin veya C derleyicisinin komut satırında belirtilen kaynak dosyadır

ve derleme zamanında kaynak dosya sunumunu (tam yol / göreli yol / taban adı) değiştirerek dosya adını nasıl görüntülemek istediğinizi denetleyin.


6
fark etmez. Ben kullandım __FILE__ve __BASE_FILE__ancak her ikisi de dosyaya tam yol gösteriyor
mahmood

derleyiciyi nasıl çağırıyorsunuz?
ziu

2
Öyleyse SCONS'un böyle gcc çağırdığına eminim gcc /absolute/path/to/file.c. Bu davranışı değiştirmenin bir yolunu bulursanız (SO, lol? Üzerinde başka bir soru açılıyor), çalışma zamanında dizeyi değiştirmeniz gerekmez
ziu

17
Bu cevap% 100 yanlış. __BASE_FILE__(dokümanlar söylediği gibi, pek açık olsa) adını üreten komut satırında belirtilen dosyada , örneğin test.cya /tmp/test.csen derleyici çağrılan şekline bağlı. __FILE__Bir başlık dosyasının içindeyseniz, tam olarak aynı şeydir , bu durumda __FILE__mevcut dosyanın adını (ör. foo.h) __BASE_FILE__Üretmeye devam ederken üretir test.c.
Quuxplusone

0

Dize kitaplığı olmayan ortamlar için çalışan bir çözüm (Linux çekirdeği, gömülü sistemler vb.):

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

Şimdi FILENAMEyerine kullanın __FILENAME__. Evet, hala çalışma zamanı bir şey ama işe yarıyor.


Platforma bağlı olarak '/' ve '\' işaretlerini kontrol etmek isteyebilirsiniz.
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

Hem Windows hem de * nix için kısa, çalışan bir cevap:

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

Neden std::max<const char*>sadece yerine ihtiyacın var std::max?
chqrlie
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.