__FILE__宏显示完整路径

mah*_*ood 152 c macros file path

C中可用的标准预定义MACRO __FILE__显示文件的完整路径.有没有办法缩短路径?我的意思是代替

/full/path/to/file.c
Run Code Online (Sandbox Code Playgroud)

我知道了

to/file.c
Run Code Online (Sandbox Code Playgroud)

要么

file.c
Run Code Online (Sandbox Code Playgroud)

red*_*ynx 154

尝试

#include <string.h>

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
Run Code Online (Sandbox Code Playgroud)

对于Windows,使用'\\'而不是'/'.

  • 这可以缩短为`strrchr("/"__ FILE __,'/')+ 1`.通过在"__FILE__"之前加上"/",strrchr可以保证找到一些东西,因此不再需要条件?:. (11认同)
  • `/`是Windows中的有效路径分隔符. (10认同)
  • `/`是传递给CreateFile()的文件名中的有效路径分隔符,依此类推.但是,这并不总是意味着你可以在Windows中的任何地方使用`/`,因为在命令提示符下使用`/`作为参数引导的传统(继承自CPM).但是质量工具会小心地将文件名分成*slash和反斜杠字符,以避免任何设法使用`/`的人出现问题. (10认同)
  • @AmigableClarkKant,不,你可以在同一个文件名中混合使用两个分隔符. (4认同)
  • 对于那些对 *strrchr* 和 *strstr* 在编译时工作感兴趣的人,在这种情况下,使用 gcc 6.3.0 -O2 进行了测试。 (4认同)
  • 如果你的平台支持它``char*fileName = basename(__ FILE __);`它肯定存在于Linux和OS X中,但不知道Windows. (2认同)
  • 这确实有效,但不幸的是您仍然会在可执行文件中找到完整路径。 (2认同)
  • 我想知道优化器是否能够优化对strchr和strrchr的调用。更不用说对`__FILENAME__`宏的引用了。如果不是,则此解决方案会100%膨胀。 (2认同)

Pat*_*ick 52

如果你正在使用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 $<))\"'")
Run Code Online (Sandbox Code Playgroud)

如果你正在使用GNU make,我认为没有理由你不能将它扩展到你自己的makefile.例如,您可能有这样一行:

CXX_FLAGS+=-D__FILENAME__='\"$(subst $(SOURCE_PREFIX)/,,$(abspath $<))\"'"
Run Code Online (Sandbox Code Playgroud)

$(SOURCE_PREFIX)您要删除的前缀在哪里.

然后用来__FILENAME__代替__FILE__.

  • 我担心这对头文件中引用的__FILE__不起作用. (10认同)
  • @firegurafiku在嵌入式平台上,代码大小通常更多是约束而不是速度.如果每个文件中都有调试语句,则可以使用完整路径字符串快速增加文件大小.我现在正在处理这样一个平台,并且引用`__FILE__`到处都是代码空间. (4认同)
  • 这个答案的解决方案不可移植,因为它使用Bourne shell转义.最好使用CMake以干净和便携的方式实现它.请参阅[define_file_basename_for_sources宏答案](http://stackoverflow.com/a/27990434/994153). (3认同)
  • GNU Make变体是CFLAGS + = -D__FILENAME __ = \"$(notdir $ <)\" (3认同)
  • 同意@BaiyanHuang,但不确定注释是否清晰。__FILE__不是一个简单的预处理器符号,它更改为当前文件,通常用于发出当前文件的名称(标题或源模块)。这个__FILENAME__只会有最外层的来源 (2认同)
  • 不要使用 cmake + ninja: ninja: error: build.ninja:221: bad $-escape (literal $ must be write as $$) (2认同)
  • ⚠️当您遵循此方法时,您会破坏 Visual Studio 的(截至 2019 年)/MP 并行编译。由于它批量编译并且具有每个文件的编译选项降级为大小为 1 的批次。请参阅 https://randomascii.wordpress.com/2014/03/22/make-vc-compiles-fast-through-parallel-compilation/ (2认同)
  • 我从非 GNU make 中得到“在非后缀规则上下文中使用 `$&lt;` 是 GNUmake 习惯用法”。我用 `$(notdir $file)` 代替。 (2认同)
  • 除了仅适用于 make 生成器之外,从 CMake 3.20 开始,这将不再适用。仍然特定于 make 的替代方法是: `set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__FILENAME__='\"$(notdir $(abspath $@))\"'")` (2认同)

Ren*_*sch 21

我刚刚想到了一个适用于源文件和头文件的优秀解决方案,非常高效,并且可以在所有平台上编译时无需编译器特定的扩展.此解决方案还保留项目的相对目录结构,因此您知道文件所在的文件夹,并且只知道项目的根目录.

我们的想法是使用您的构建工具获取源目录的大小,然后将其添加到__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}")
Run Code Online (Sandbox Code Playgroud)

在源代码中,定义一个__FILENAME__宏,只是将源路径大小添加到__FILE__宏:

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)
Run Code Online (Sandbox Code Playgroud)

然后只使用这个新宏而不是__FILE__宏.这是有效的,因为__FILE__路径始终以CMake源目录的路径开头.通过从__FILE__字符串中删除它,预处理器将负责指定正确的文件名,它将全部相对于CMake项目的根目录.

如果你关心的表现,这是因为使用高效__FILE__,因为无论__FILE__SOURCE_PATH_SIZE已知的编译时间常数,因此它可以被编译器优化掉.

唯一会失败的地方是你在生成的文件上使用它并且它们位于一个非源的构建文件夹中.那么你可能不得不使用CMAKE_BUILD_DIR变量来创建另一个宏而不是CMAKE_SOURCE_DIR.

  • 我起初并不理解这一点.我编写了示例并将它们与gcc和clang一起运行并且它们可以工作.我还尝试添加各种文字数值,并且表现得像我预期的那样.然后它终于恍然大悟了.`__FILE__`是指向字节数组的指针.因此,添加数字文字只是指针添加.非常聪明@RenatoUtsch (3认同)
  • 仅当源文件位于 cmake list 目录下时它才有效。如果源文件位于外部,则它会损坏,可能会在文字字符串之外进行访问。所以要小心。 (2认同)

Sev*_*yev 16

在这里纯粹编译时间解决方案.它基于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__)
Run Code Online (Sandbox Code Playgroud)

随意将条件运算符级联扩展到项目中的最大敏感文件名.路径长度无关紧要,只要您从字符串末尾检查足够远即可.

我会看看是否可以获得一个类似的宏,没有硬编码长度的宏递归...

  • 很酷的答案。您至少需要使用-O1才能在编译时使用它。 (3认同)
  • 这不是倒退了吗?您想要找到*最后*出现的“/”,这意味着您应该首先从“sizeof(s) &gt; 2”检查开始。另外,这对我来说在编译时(-Os)不起作用。完整路径字符串存在于输出二进制文件中。 (2认同)

And*_*dry 15

  • 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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    '

    int main()
    {
         printf("%s\n", &__FILE__[get_file_name_offset(__FILE__)]);
    }
    
    Run Code Online (Sandbox Code Playgroud)

在以下情况下,代码会生成编译时间偏移:

  • gcc: 至少 gcc6.1 + -O1
  • msvc:将结果放入 constexpr 变量:

      constexpr auto file = &__FILE__[get_file_name_offset(__FILE__)];
      printf("%s\n", file);
    
    Run Code Online (Sandbox Code Playgroud)
  • 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__))]);
    }
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/u6s8j3


Kei*_*son 11

至少对于gcc,值__FILE__编译器命令行中指定的文件路径.如果您这样编译file.c:

gcc -c /full/path/to/file.c
Run Code Online (Sandbox Code Playgroud)

__FILE__将扩大到"/full/path/to/file.c".如果你这样做:

cd /full/path/to
gcc -c file.c
Run Code Online (Sandbox Code Playgroud)

然后__FILE__将扩展到公正"file.c".

这可能是也可能不实际.

C标准不要求这种行为.所有它说的__FILE__是它扩展为"当前源文件的假定名称(字符串文字)".

另一种方法是使用该#line指令.它会覆盖当前行号,也可以覆盖源文件名.如果要覆盖文件名但仅保留行号,请使用__LINE__宏.

例如,您可以在顶部附近添加file.c:

#line __LINE__ "file.c"
Run Code Online (Sandbox Code Playgroud)

唯一的问题是它将指定的行号分配给下一行,并且第一个参数#line必须是一个数字序列,所以你不能做类似的事情

#line (__LINE__-1) "file.c"  // This is invalid
Run Code Online (Sandbox Code Playgroud)

确保#line指令中的文件名与文件的实际名称相匹配,这是一个练习.

至少对于gcc,这也会影响诊断消息中报告的文件名.

  • 基思汤普森这是一个很好的解决方案,谢谢。唯一的问题是这个宏似乎将 `__LINE__` 值减一。所以在`x` 行中写的`__LINE__` 被评估为`x-1`。至少使用 gcc 5.4。 (2认同)
  • @Klepak:你说得对,这是标准行为。该指令“使实现的行为就像以下源代码行序列以具有由数字序列指定的行号的源代码行开头一样”。它必须是一个*数字序列*,所以你不能使用`#line __LINE__-1, "file.c"`。我会更新我的答案。 (2认同)
  • 在文件的第 0 行,我只需执行:`#line 2 "name.cpp"`。另外,作为旁注,很高兴知道它不会污染“#include”它的其他文件(至少在 MSVC 上) (2认同)

Pau*_*aul 9

对于 Visual Studio,您可以使用该/d1trimfile选项。

您可能想将其设置为/d1trimfile:"$(SolutionDir)\"

魔法

注意:\对 之前尾随的解释"

要转义第一个$(SolutionDir)以反斜杠结尾),否则引号将被转义。引用前需要均匀数量的反斜杠。


小智 7

在 VC 中,当使用 时/FC__FILE__扩展到完整路径,没有/FC选项__FILE__扩展文件名。参考:这里


Cha*_*ger 7

此行为已在某些编译器中实现,您可以使用以下方法获取编译文件的基本名称:


Dmi*_*don 6

以下是 C++14 或更高版本中使用编译时计算的解决方案:

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__);
Run Code Online (Sandbox Code Playgroud)


sim*_*tts 6

晚会晚了一点,但是对于海湾合作委员会,请看看以下-ffile-prefix-map=old=new选项:

编译位于old目录中的文件时,请在编译结果中记录对它们的所有引用,就好像这些文件位于new目录中一样。指定此选项等效于指定所有单个-f*-prefix-map选项。这可用于制作与位置无关的可复制构建。另请参阅 -fmacro-prefix-map-fdebug-prefix-map

因此,对于我的Jenkins构建,我将添加-ffile-prefix-map=${WORKSPACE}/=/,另一个将删除本地dev软件包的安装前缀。

注意不幸的是,该-ffile-prefix-map选项仅在GCC 8及更高版本中可用-fmacro-prefix-map我认为正是这一点__FILE__。例如,对于GCC 5,我们只有-fdebug-prefix-map(不会)影响__FILE__

  • 选项“-ffile-prefix-map”确实意味着“-fdebug-prefix-map”和“-fmacro-prefix-map”选项。另请参阅 https://reproducible-builds.org/docs/build-path/ 上的参考资料 跟踪“-fmacro-prefix-map”和“-ffile-prefix-map”的 GCC 错误是 https://gcc。 gnu.org/bugzilla/show_bug.cgi?id=70268 (4认同)
  • 如果您的编译器支持此功能,就像任何最新版本的 GCC 或 Clang 一样,您应该使用此解决方案而不是其他解决方案。它更加优雅和轻便。 (2认同)

Lev*_*i.G 6

多年来,我对@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 $<))\"'")
Run Code Online (Sandbox Code Playgroud)

为什么要使用这个?

  • -Wno-builtin-macro-redefined使重新定义__FILE__宏的编译器警告静音。

    对于那些不支持的编译器,请参考下面的Robust 方式

  • 从文件路径中剥离项目路径是您真正的要求。您不想浪费时间找出header.h文件src/foo/header.hsrc/bar/header.h.

  • 我们应该删除配置文件中的__FILE__cmake

    该宏用于大多数现有代码。只需重新定义它就可以让您自由。

    编译器喜欢gcc从命令行参数预定义这个宏。并且完整路径写在makefiles 生成的 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)
Run Code Online (Sandbox Code Playgroud)

请记住从该set(*_FLAGS ... -D__FILE__=...)行中删除此编译器选项。


str*_*ner 6

最近的 Clang 编译器有一个__FILE_NAME__宏(见这里)。

  • 似乎 gcc 也[添加了它](https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=1a9b3f04c11eb467a8dc504a37dad57a371a0d4c) (2认同)

Sea*_*ean 5

没有编译时的方法来做到这一点。显然,您可以在运行时使用 C 运行时执行此操作,正如其他一些答案所证明的那样,但是在编译时,当预处理器启动时,您就不走运了。

  • 它可能不是性能关键,但很容易被视为隐私关键。没有真正好的理由在冻结到已发布的 EXE 文件中的字符串中揭示我的每个客户的组织实践。更糟糕的是,对于代表客户创建的应用程序,这些字符串可能会透露我的客户可能不愿意看到的内容,例如不是他们自己产品的作者。由于 `__FILE__` 是由 `assert()` 隐式调用的,所以这种泄漏可以在没有任何其他公开行为的情况下发生。 (5认同)