web-dev-qa-db-ja.com

__FILE__マクロは完全なパスを示します

Cで利用可能な標準の事前定義済みMACRO __FILE__は、ファイルへのフルパスを示しています。パスを短くする方法はありますか?代わりに

/full/path/to/file.c

そうですか

to/file.c

または

file.c
142
mahmood

試してみる

#include <string.h>

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

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

143
red1ynx

Cmakeを使用している場合のヒントを次に示します。 From: 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__を使用します。

50
Patrick

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

アイデアは、ビルドツールでソースディレクトリのサイズを取得し、それを__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変数を使用して別のマクロを作成する必要があります。

21
RenatoUtsch

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

条件演算子カスケードをプロジェクト内の意味のある最大ファイル名まで自由に拡張してください。文字列の最後から十分にチェックする限り、パスの長さは重要ではありません。

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

11
Seva Alekseyev

少なくとも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"

これに関する唯一の問題は、指定された行番号をfollowing行に割り当て、#lineへの最初の引数がdigit-sequenceでなければならないことです。のようなことをする

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

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

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

11
Keith Thompson

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

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

basename() 関数を使用するか、Windowsの場合は _ splitpath() を使用します。

#include <libgen.h>

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

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

4
Prof. Falken

vsと、/ FCを使用する場合、FILEは、/ FCを使用しないフルパスと等しいFILEはファイル名と同じです。 ref ここ

4
user7382523

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

4
Sean

@ 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
hmjd
  • C++ 11
  • msvc2015u3、gcc5.4、clang3.8.0

    template <size_t S>
    inline constexpr size_t get_file_name_offset(const char (& 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 <size_t S>
    inline constexpr size_t get_file_name_offset(const wchar_t (& str)[S], size_t i = S - 1)
    {
        return (str[i] == L'/' || str[i] == L'\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
    }
    
    template <size_t S>
    inline constexpr size_t get_file_name_offset(const char16_t (& str)[S], size_t i = S - 1)
    {
        return (str[i] == u'/' || str[i] == u'\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
    }
    
    template <size_t S>
    inline constexpr size_t get_file_name_offset(const char32_t (& str)[S], size_t i = S - 1)
    {
        return (str[i] == U'/' || str[i] == U'\\') ? 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/P1y3ZI

3
Andry

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

。hファイルで静的なグローバル変数を定義することで簡単に行えます。この定義では、。hを含む。cppファイルごとに個別の独立した変数を指定します。マルチスレッドに対応するには、変数もスレッドローカル(TLS)にする価値があります。

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;
}
3
Kunis

パーティーに少し遅れましたが、GCCについては-ffile-prefix-map=old=newオプションを見てください:

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

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

NOTE残念ながら、-ffile-prefix-mapオプションは、GCC 8以降でのみ使用可能です。また、-fmacro-prefix-mapは、I think__FILE__部分を実行します。たとえば、GCC 5では、-fdebug-prefix-mapのみがあり、__FILE__には影響を与えません(表示されません)。

3
simon.watts

試してみる

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

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

#pragma pop_macro("__FILE__")

ソースファイルの最後。

2
Alain Totouom

fILEマクロを少し改善したいと思っています。

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

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

2
alexander golks

このページに移動して、出荷しているバイナリからいビルド場所を指している絶対ソースパスを削除する方法を探している場合は、以下がニーズに合っている可能性があります。

これは、著者が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で動作しました

1
IvanR

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

何年もの間、@ Patrickの answer で同じソリューションを使用しました。

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

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

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__マクロを再定義するためのコンパイラの警告をミュートします。

    これらのコンパイラはこれをサポートしていませんので、以下のRobust wayを参照してください。

  • real要件であるファイルパスからプロジェクトパスを削除します。 header.hファイル、src/foo/header.h、またはsrc/bar/header.hがどこにあるかを見つけるために時間を無駄にしたくないでしょう。

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

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

    gccなどのコンパイラは、コマンドライン引数からこのマクロを事前定義します。そして、フルパスはmakefileによって生成されたcmakesに書き込まれます。

  • 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__=...)行からこのコンパイラオプションを忘れずに削除してください。

0
Levi.G
#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
Dimfred

文字列ライブラリを持たない環境(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
beavis9k

GCCを使用しているため、 活用する of

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

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

0
ziu

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

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__);
0
Dmitry Gordon