__FILE__マクロはフルパスを表示します


164

__FILE__Cで利用可能な標準の定義済みマクロは、ファイルへの絶対パスを示します。パスを短くする方法はありますか?代わりに

/full/path/to/file.c

そうですか

to/file.c

または

file.c

18
プリプロセッサのみのソリューションを見つけることは本当に素晴らしいでしょう。文字列操作に基づく提案は実行時に実行されると思います。
cdleonard

9
gccを使用しているので__FILE__、コマンドラインで渡すファイル名を変更することで、何が含まれるかを変更できると思います。したがって、の代わりにgcc /full/path/to/file.c、を試してくださいcd /full/path/to; gcc file.c; cd -;。もちろん、インクルードパスまたは出力ファイルの場所をgccの現在のディレクトリに依存している場合は、それだけではありません。編集:gccのドキュメントで、入力ファイル名の引数ではなく、完全なパスであることが示唆されていますが、Cygwinのgcc 4.5.3の場合はそうではありません。Linuxで試してみてください。
Steve Jessop、2011

4
GCC 4.5.1(特にarm-none-eabi用に構築)は、コマンドラインでファイル名の正確なテキストを使用します。私の場合、現在のディレクトリを適切な場所(プロジェクトファイルの場所、など)に設定したり、そこからの相対パスを使用したりせずに、すべてのファイル名を完全修飾してGCCを呼び出すのはIDEの責任でした。私は多くのIDEが(特にWindowsで)「現在の」ディレクトリが本当にGUIアプリ用である場所を説明することに関連するある種の不快感からそれをしていると思います。
RBerteig 2012

3
@SteveJessop-このコメントを読んでほしい。__FILE__印刷されたように見える状況があり../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.c、gccがファイルをコンパイルした場所を確認したいので、このファイルは表示されているように相対的に配置されています。
Chan Kim

1
この質問は、リンクされた質問の真似ではありません。1つには、リンクされているのはC ++に関するものであり、その結果、C ++マクロの難問について詳しく説明しています。次に、OPの質問には、マクロソリューションを義務付けるものは何もありません。それは厳粛に問題を指摘し、自由回答形式の質問をします。
Falken教授、2015年

回答:


166

試す

#include <string.h>

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

Windowsでは、「/」の代わりに「\\」を使用します。


13
/Windowsでは有効なパス区切り文字です。
ハンスパッサント

11
/CreateFile()などに渡されるファイル名の有効なパス区切り文字です。ただし、コマンドプロンプトで引数として/使用するという(CPMから継承された)伝統があるため、Windowsのどこでも使用できるとは限りません/。しかし、品質の高いツールでは、ファイル名をスラッシュとバックスラッシュの両方で分割して、なんとか使用できる人が問題を回避できるようにします/
RBerteig 2012

5
@AmigableClarkKant、同じファイル名に両方のセパレータを混在させることはできません。
RBerteig 2012

2
あなたのプラットフォームがそれをサポートしているなら、char* fileName = basename(__FILE__); それは間違いなくLinuxとOS Xにありますが、Windowsについては知らないのです。
JeremyP 2013

14
これはに短縮できますstrrchr("/" __FILE__, '/') + 1。先頭に「/」を__FILE__付加することにより、strrchrは何かを見つけることが保証されるため、条件??は不要になります。
ɲeuroburɳ

54

cmakeを使用している場合のヒントを次に示します。から: 送信元 http //public.kitware.com/pipermail/cmake/2013-January/053117.html

ヒントをコピーしているので、すべてこのページにあります。

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

GNU makeを使用している場合、これを独自のmakefileに拡張できなかった理由はわかりません。たとえば、次のような行があるとします。

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

どこ $(SOURCE_PREFIX)削除するという接頭辞があります。

次に__FILENAME__、の代わりに使用し__FILE__ます。


11
申し訳ありませんが、これはヘッダーファイルで参照されているFILEでは機能しません。
Baiyan Huang 2013

2
@BaiyanHuangに同意しますが、コメントが明確かどうかはわかりません。 __FILE__は単純なプリプロセッサシンボルではありません。現在のファイルへの変更は、現在のファイル(ヘッダーまたはソースモジュール)の名前を出力するためによく使用されます。これに__FILENAME__は最も外側のソースしかありません
nhed

3
この回答のソリューションはBourneシェルエスケープを使用しているため、移植性がありません。クリーンでポータブルな方法で実装するには、CMakeを使用する方がよいでしょう。ここでdefine_file_basename_for_sourcesマクロの回答を参照してください。
コリンDベネット

3
これのGNU MakeバリアントはCFLAGS + = -D__FILENAME __ = \ "$(notdir $ <)\"です
ctuffli

4
@firegurafiku組み込みプラットフォームでは、多くの場合、コードサイズは速度よりも制約事項です。各ファイルにデバッグ文がある場合は、フルパス文字列を使用してファイルサイズをすばやく拡大できます。私は現在そのようなプラットフォームを扱っており、__FILE__どこでも参照することはコード空間を吹き飛ばしています。
mrtumnus

26

私は、ソースファイルとヘッダーファイルの両方で機能し、非常に効率的で、コンパイラ固有の拡張機能なしですべてのプラットフォームのコンパイル時に機能する、これに対する優れたソリューションを考えました。このソリューションでは、プロジェクトの相対ディレクトリ構造も保持されるため、ファイルがどのフォルダーにあり、プロジェクトのルートに対してのみ相対的であるかがわかります。

アイデアは、ビルドツールでソースディレクトリのサイズを取得し、それを単に __FILE__マクロにディレクトリを完全に削除し、ソースディレクトリから始まるファイル名のみを表示します。

次の例はCMakeを使用して実装されていますが、トリックが非常に単純であるため、他のビルドツールで動作しない理由はありません。

CMakeLists.txtファイルで、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}")

ソースコードで、__FILENAME__マクロにソースパスサイズを追加するだけのマクロを定義します__FILE__

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)

次に、__FILE__マクロの代わりにこの新しいマクロを使用します。__FILE__パスは常にCMakeソースディレクトリへのパスで始まるため、これは機能します。__FILE__文字列から削除することにより、プリプロセッサが正しいファイル名を指定し、すべてCMakeプロジェクトのルートからの相対パスになります。

パフォーマンスを気にする場合は、とを使用するのと同じくらい効率的です__FILE__。なぜなら、__FILE__SOURCE_PATH_SIZEは既知のコンパイル時定数なので、コンパイラーによって最適化することができるからです。

これが失敗する唯一の場所は、生成されたファイルでこれを使用していて、それらがオフソースのビルドフォルダー上にある場合です。次に、おそらくのCMAKE_BUILD_DIR代わりに変数を使用して別のマクロを作成する必要がありますCMAKE_SOURCE_DIR


4
最初はこれがわかりませんでした。私はサンプルをコード化し、gccとclangに対して実行しましたが、それらは機能します。また、さまざまなリテラル数値を追加するだけでも実験しましたが、期待どおりに動作します。それから、ついにそれは私に現れました。__FILE__バイトの配列へのポインタです。したがって、数値リテラルの追加は、単なるポインタの追加です。非常に賢い@RenatoUtsch
Daniel

ソースファイルがcmakeリストディレクトリにある場合にのみ機能します。ソースファイルが外部にある場合、壊れます。リテラル文字列の外部にアクセスする可能性があります。ですので注意してください。
Andry

実際にはコードサイズは小さくなりません。パス全体がバイナリにコンパイルされていると思いますが、ポインタだけが変更されています。
タリオン

__FILE__マクロとSOURCE_PATH_SIZEマクロはどちらも、コンパイル時に既知の定数です。私は、最新の最適化コンパイラーがストリングの一部が使用されていないことを検出し、単にそれをバイナリーから削除できることを期待しています。とにかく、これらの数バイトがバイナリサイズに大きな違いをもたらすとは思わないので、私はそれについてはあまり気にしません。
RenatoUtsch

@RenatoUtsch私が取り組んでいるプロジェクトには、ファイル名を指定するだけの変更がありますが、ヘッダーにもCファイル名を付けるという欠点があります。この変更は、再現可能なビルドを取得するために行われました。では、-O2を指定したgccを使用すると、実際に文字列が最適化され、ビルドが再現可能になりますか?
Paul Stelian

18

ここで純粋にコンパイル時間ソリューション。sizeof()文字列リテラルがその長さ+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__)

条件付き演算子のカスケードをプロジェクト内の適切な最大ファイル名に拡張してください。文字列の末尾から十分離れているかぎり、パスの長さは関係ありません。

マクロ再帰で長さがハードコーディングされていない同様のマクロを取得できるかどうかを確認します...


3
クールな答え。-O1これをコンパイル時に使用するには、少なくともを使用する必要があります。
Digital Trauma 2017年

1
これは逆ではないですか?最後に出現する '/' を検索する必要があるため、sizeof(s) > 2最初にチェックから開始する必要があります。また、これはコンパイル時の-Osでは機能しませんでした。フルパス文字列は出力バイナリに存在していました。
mrtumnus

15

少なくともgccの場合、の値は、コンパイラのコマンドラインで指定された__FILE__ファイルパスです。次のようにコンパイルすると、file.c

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

これ__FILE__はに拡張され"/full/path/to/file.c"ます。代わりにこれを行う場合:

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

その後、__FILE__に拡大されます"file.c"

これは実用的である場合とそうでない場合があります。

C標準では、この動作は必要ありません。それが言っているの__FILE__は、それが「現在のソースファイルの推定名(文字列リテラル)」に拡張されるということだけです。

別の方法は、#lineディレクティブを使用することです。現在の行番号、およびオプションでソースファイル名を上書きします。ファイル名を上書きし、行番号はそのままにしたい場合は、__LINE__マクロを使用します。

たとえば、これをの上部近くに追加できますfile.c

#line __LINE__ "file.c"

これの唯一の問題は、指定された行番号を次の行に割り当て、最初の引数#line数字列でなければならないため、次のようなことはできないということです

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

#lineディレクティブのファイル名が実際のファイル名と一致することを確認することは、演習として残します。

少なくともgccの場合、これは診断メッセージで報告されるファイル名にも影響します。


1
キース・トンプソンは素晴らしいソリューションです、ありがとう。唯一の問題は、このマクロが__LINE__値を1つ減らしているように見えることです。したがって__LINE__、にx評価された行にgestで記述されていx-1ます。少なくともgcc 5.4では。
elklepo

1
@クレパック:その通りで、それが標準的な振る舞いです。ディレクティブは、「次の一連のソース行が、数字列で指定された行番号を持つソース行で始まるかのように動作するように実装を引き起こします」。そして、それは数字列でなければならないので、あなたは使うことができません#line __LINE__-1, "file.c"。回答を更新します。
キーストンプソン

1
Clang、MSVC、Intel Cコンパイラなどの他のコンパイラはどうでしょうか?
フランクリンYu

8

パーティーには少し遅れますが、GCC-ffile-prefix-map=old=new場合はオプションを確認してください。

oldディレクトリにあるファイルをコンパイルするときは、ファイルが代わりにnewディレクトリにあるかのように、コンパイルの結果にそれらへの参照を記録します。このオプションを指定することは、個々の-f*-prefix-mapオプションをすべて指定することと同じです。これは、場所に依存しない再現可能なビルドを作成するために使用できます。-fmacro-prefix-mapおよびも参照してください -fdebug-prefix-map

したがって、私のJenkinsビルドにはを追加し-ffile-prefix-map=${WORKSPACE}/=/、もう1つはローカル開発パッケージのインストールプレフィックスを削除します。

NOTEは残念ながら-ffile-prefix-mapあるとしてオプションは、以降のGCC 8にのみ有りで-fmacro-prefix-mapいる、私は考えてない、__FILE__一部を。たとえば、GCC 5には、に-fdebug-prefix-map影響を与えない(表示される)ものだけが含まれています__FILE__


1
オプションは、-ffile-prefix-map実際に両方の意味-fdebug-prefix-map-fmacro-prefix-mapオプションを。言及も参照してくださいreproducible-builds.org/docs/build-pathにトラックというGCCのバグを-fmacro-prefix-mapしては-ffile-prefix-mapいる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__)]);
    }

コードは次の場合にコンパイル時間オフセットを生成します。

  • gcc:少なくともgcc6.1 + -O1
  • msvc:結果をconstexpr変数に入れます:

      constexpr auto file = &__FILE__[get_file_name_offset(__FILE__)];
      printf("%s\n", file);
  • clang:コンパイル時評価ではない

最適化が無効になっているデバッグ構成でも、3つのコンパイラすべてにコンパイル時の評価を強制するトリックがあります。

    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


男、それは美しくてうまくいきます、ありがとう!この答えがそれほど過小評価されている理由はわかりません。
ローマ

5

VCでは、使用している場合は/FC__FILE__なし、完全なパスに展開/FCオプション__FILE__が拡張ファイル名。ref:ここに


4

これを行うコンパイル時の方法はありません。他のいくつかの回答が示しているように、実行時にCランタイムを使用して実行できることは明らかですが、コンパイル時にプリプロセッサーが起動すると、運が悪くなります。


1
strrchr答えはもっともらしくものの、もちろん、まだありませんプリプロセッサによって、コンパイル時に計算することができます。gccが実際にそれを行うかどうかはわかりませんが、チェックしていませんが、strlenコンパイル時に文字列リテラルの計算を行うことは確かです。
Steve Jessop、2011

@Steve-多分、しかしそれはコンパイラ固有の振る舞いへの大きな依存です。
Sean

このコードはパフォーマンスが重要であることを私は非常に疑うので、それは大きな依存関係であるとは思わない。そうであれば、ループの外に移動します。これが大きな問題である場合、ベース名だけを含む文字列リテラルが絶対に必要なので、ソースに対してスクリプトを実行することにより、ビルド時に正しい文字列を計算できます。
Steve Jessop、2011

5
パフォーマンスは重要ではないかもしれませんが、プライバシーが重要であると簡単に見なすことができます。リリースされたEXEファイルにフリーズされた文字列で、顧客ごとの組織の慣行を明らかにする正当な理由はありません。さらに悪いことに、顧客に代わって作成されたアプリケーションの場合、これらの文字列は、自分の製品の作者ではないなど、私の顧客が好まないかもしれないことを明らかにする可能性があります。__FILE__はによって暗黙的に呼び出されるためassert()、このリークは他の明白な動作なしで発生する可能性があります。
RBerteig 2012

@RBerteig __FILE__自体のベース名も、顧客が好まない可能性があることを明らかにする可能性があるため__FILE__、完全な絶対パス名が含まれていても、ベース名だけが含まれていても、どこでも使用すると、指摘したのと同じ問題があります。この状況では、すべての出力を精査する必要があり、顧客への出力用に特別なAPIを導入する必要があります。残りの出力は/ dev / NULLまたはstdoutにダンプされ、stderrは閉じられるはずです。:-)
2013年

4

basename()関数を使用します。Windowsの場合は、_splitpath()を使用します。

#include <libgen.h>

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

またman 3 basename、シェルで試してください。


2
@mahmood: char file_copy[] = __FILE__; const char *filename = basename(__FILE__);。コピーの理由は、basenameが入力文字列を変更できるためです。また、結果ポインタは、basenameが再度呼び出されるまでのみ有効であることにも注意する必要があります。つまり、スレッドセーフではありません。
Steve Jessop、2011

@SteveJessop、ああ、忘れました。そうだね。
Falken教授、2011

1
@Amigable:公平を期すためにbasename、実際にはからの入力文字列は変更されないのではないかと思い__FILE__ます。入力文字列/の末尾にa がないため、変更する必要がないためです。だからあなたはそれでうまくいくかもしれませんがbasename、誰かが初めて見たとき、彼らはすべての制限付きでそれを見るはずです。
Steve Jessop、2011

@SteveJessop basename()のBSdマニュアルページでは、basename()のレガシーバージョンはconst char *を受け取り、文字列を変更しないと述べています。Linuxのmanページでは、constについては何も触れられていませんが、引数文字列の一部を返すことができると述べています。したがって、basename()は慎重に扱ってください。
Falken教授、2011

@SteveJessopは、笑、私は今、慎重にあなたのコメントを見た後、4年後に、実現することを/文字列手段のベース名の末尾にその引数を変更するための十分な理由を有することができます。
Falken教授、

4

CMAKEGNUコンパイラを使用している場合、このglobal定義は正常に機能します。

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

4

@Patrickの回答で同じソリューションを何年も使用しています。

フルパスにシンボルリンクが含まれている場合、小さな問題があります。

より良いソリューション。

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

なぜこれを使うべきですか?

  • -Wno-builtin-macro-redefined__FILE__マクロを再定義するためのコンパイラ警告をミュートします。

    これらのコンパイラがこれをサポートしていない場合は、以下の堅牢な方法を参照してください。

  • 実際の要件は、ファイルパスからプロジェクトパスを取り除くことです。あなたはここで見つけるために時間を無駄に好きではないだろうheader.hファイル、src/foo/header.hまたはsrc/bar/header.h

  • __FILE__マクロを削除する必要がありますcmakeファイルでます。

    このマクロは、ほとんどの既存のコードで使用されます。単にそれを再定義するだけであなたを自由にすることができます。

    のようなコンパイラgccは、コマンドライン引数からこのマクロを事前定義します。また、フルパスはによってmakefile生成されたs で記述されcmakeます。

  • のハードコードCMAKE_*_FLAGSが必要です。

    やなどadd_definitions()、最近のバージョンにコンパイラオプションまたは定義を追加するコマンドがありますadd_compile_definitions()。これらのコマンドは、substソースファイルに適用する前のようにmake関数を解析します。それは私たちが望むことではありません。

の堅牢な方法-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)

このコンパイラオプションをset(*_FLAGS ... -D__FILE__=...)行から削除することを忘れないでください。


これは、インクルードファイルからのコンテンツには機能しません。
chqrlie

コードを投稿できますか?一般的なケースは、ローカルスコープで変数を設定し、それを別のスコープで使用することです。
Levi.G

たとえば__FILE__、ヘッダーファイルで定義されたインライン関数で診断を生成するために定義を使用する場合、ランタイム診断は、インクルードファイルの名前ではなく、コンパイラーに渡されたファイルの名前を報告しますが、行番号はインクルードファイルを参照してください。
chqrlie

はい、それはそうなるように設計されています、最も一般的な使用法は#define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args)です。LOG()マクロを使用する場合log.h、メッセージで実際に表示する必要はありません。結局のところ、__FILE__マクロは含まれているファイルではなく、すべてのC / Cppファイル(コンパイル単位)で展開されます。
Levi.G

3

@ red1ynxが提案したものを少し変更すると、次のマクロが作成されます。

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

各.c(pp)ファイルに以下を追加します。

SET_THIS_FILE_NAME();

その後、次のTHIS_FILE_NAME代わりに参照できます__FILE__

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

これは、マクロが参照されるたびではなく、.c(pp)ファイルごとに1回実行されることを意味します。

.c(pp)ファイルからの使用に限定されており、ヘッダーファイルからは使用できません。


3

マクロした __FILENAME__毎回フルパスをカットしないようにを実行しました。問題は、結果のファイル名をcpp-local変数に保持することです。

これは、.hファイルで静的グローバル変数を定義することで簡単に実行できます。この定義は、.hを含む各.cppファイルに個別の独立した変数を与えます。マルチスレッディングに対応するには、変数をローカルスレッド(TLS)にすることも重要です。

1つの変数はファイル名(縮小)を格納します。もう1つは、__FILE__与えられた非カット値を保持します。hファイル:

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

マクロ自体がすべてのロジックでメソッドを呼び出します。

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

そして、関数はこのように実装されています:

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()はさまざまな方法で実装できますが、高速で簡単なのは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

FILEマクロを少し改善したいだけです:

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

これは/と\をキャッチし、Czarek Tomczakが要求したように、これは私の混合環境でうまく機能します。


6
という名前のマクロを定義するFILEことは、を含める場合、非常に悪い考えです<stdio.h>
キース・トンプソン

知っておくと良い。私はCzarekに私の\\ /ソリューションを表示したかっただけなので、名前の付け方を気にしません。
アレクサンダーgolks 2013

2

試す

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

ソースファイルのincludeステートメントの後に追加して

#pragma pop_macro("__FILE__")

ソースファイルの最後に。


2
push_macropop_macro非標準です。(gccは、Microsoft Windowsコンパイラーとの互換性のためにそれらをサポートしています。)いずれの場合も、の定義をプッシュおよびポップしても意味があり__FILE__ません。とにかく、復元された値は、ソースファイルの終了後は使用されません。値を変更するクリーナーの方法が__FILE__ある#line __LINE__ "foobar.c"
キース・トンプソン

1
これにより、gccのプリプロセッサで内部エラーが発生します。gcc.gnu.org/bugzilla/show_bug.cgi?id=69665
キーストンプソン、

1

これは、Linux(パス '/')とWindows( '\'と '/'の混合)の両方で機能する移植可能な関数です。
gcc、clang、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;
}

標準出力:

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

コンパイル時の計算を使用するソリューションは次のとおりです。

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

配布しているバイナリから、醜いビルド場所を指している絶対ソースパスを削除する方法を探していた場合は、以下がニーズに合う可能性があります。

これは、CMakeの使用を前提としているため、作者が望んだ答えを正確に生成するものではありませんが、かなり近づいています。それは私に多くの時間を節約させたであろうので、これは誰もが以前に言及しなかったのは残念です。

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

上記の変数をに設定するONと、次の形式でビルドコマンドが生成されます。

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

その結果、__FILE__マクロは次のように解決されます../../src/path/to/source.c

CMakeのドキュメント

ただし、ドキュメントページの警告に注意してください。

相対パスを使用します(機能しない可能性があります!)。

すべてのケースで動作することは保証されていませんが、私の場合は動作しました-CMake 3.13 + gcc 4.5


CMake 3.4以降のドキュメントには、「この変数は効果がありません。以前のリリースで部分的に実装されていた効果は、CMake 3.4では削除されました。」3.13でうまくいったなら、それには別の理由があります。
mike.dld

0

あなたはGCCを使用しているので、あなたができる利点を取る

__BASE_FILE__このマクロは、C文字列定数の形式で、メイン入力ファイルの名前に展開されます。これは、プリプロセッサまたはCコンパイラのコマンドラインで指定されたソースファイルです。

次に、コンパイル時にソースファイルの表現(フルパス/相対パス/ベース名)を変更して、ファイル名の表示方法を制御します。


6
違いはありません。私が使用__FILE__しました__BASE_FILE__が、どちらもファイルへのフルパスを示しています
mahmood

どのようにコンパイラを呼び出しますか?
ziu

2
次に、SCONSがこのようにgccを呼び出しているに違いないgcc /absolute/path/to/file.c。この動作を変更する方法を見つけた場合(SOで別の質問を開いてください、笑?)、実行時に文字列を変更する必要はありません
ziu

17
この答えは100%間違っています。__BASE_FILE__(ドキュメントが不明瞭ながらも、言うように)の名前を生成し、コマンドラインで指定されたファイルを、例えばtest.cまたは/tmp/test.cあなたがコンパイラを起動した方法に応じて。これはとまったく同じ__FILE__ですが、ヘッダーファイル内にいる場合__FILE__は、現在のファイルの名前(例foo.h:)__BASE_FILE__が生成され、引き続きが生成されtest.cます。
Quuxplusone、2015年

0

文字列ライブラリがない環境(Linuxカーネル、組み込みシステムなど)で機能するソリューションは次のとおりです。

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

FILENAME代わりにを使用してください__FILENAME__。はい、それはまだランタイムのものですが、動作します。


プラットフォームによっては、「/」と「\」の両方を確認したい場合があります。
テクノフィル

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

Windowsと* nixの両方の短い実用的な答え:

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

なぜあなたstd::max<const char*>は単に代わりに必要なのstd::maxですか?
chqrlie
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.