【问题标题】:Getting base name of the source file at compile time在编译时获取源文件的基本名称
【发布时间】:2010-09-19 05:57:52
【问题描述】:

我正在使用 GCC; __FILE__ 返回当前源文件的完整路径和名称:/path/to/file.cpp。有没有办法在编译时只获取文件名file.cpp(没有路径)?是否有可能以便携的方式做到这一点?模板元编程可以应用于字符串吗?

我在错误记录宏中使用它。我真的不希望我的源代码的完整路径进入可执行文件。

【问题讨论】:

  • 文件名是给编译器的名字。
  • 我的源代码有一个目录结构,所以我不能只将文件名传递给编译器。此外,我使用的 CMake 似乎总是给出完整的路径。我认为这个问题应该保留 c++ 标签,因为模板元编程可能是一个有效的答案?
  • @betontalpfa 更像是这个是这个的副本。

标签: c++ c makefile macros compiler-construction


【解决方案1】:

如果您使用的是make 程序,您应该能够预先调整文件名并将其作为宏传递给gcc 以在您的程序中使用。例如,在您的makefile 中,更改行:

file.o: file.c
    gcc -c -o file.o src/file.c

到:

file.o: src/file.c
    gcc "-DMYFILE=\"`basename $<`\"" -c -o file.o src/file.c

这将允许您在代码中使用MYFILE 而不是__FILE__

源文件$&lt;basename的使用表示可以在.c.o等通用规则中使用。下面的代码说明了它是如何工作的。一、makefile

mainprog: main.o makefile
    gcc -o mainprog main.o

main.o: src/main.c makefile
    gcc "-DMYFILE=\"`basename $<`\"" -c -o main.o src/main.c

然后是子目录中的一个文件,src/main.c:

#include <stdio.h>

int main (int argc, char *argv[]) {
    printf ("file = %s\n", MYFILE);
    return 0;
}

最后,显示它正在运行的成绩单:

pax:~$ mainprog
file = main.c

注意file = 行,它只包含文件的基本名称,而不是目录名称。

【讨论】:

  • 请注意,以双下划线(或下划线和大写字母)开头的名称保留用于实现。最好避免使用__MYFILE__,可能使用MYFILE 或类似的东西。
【解决方案2】:

我不知道直接的方法。你可以使用:

#line 1 "filename.c"

在源文件的顶部设置__FILE__ 的值,但我不确定这是否比硬编码好得多。或者只是使用 #define 创建自己的宏。

另一个选项可能是使用 -D 和 $(shell basename $

编辑:如果您使用#define 或-D 选项,您应该创建自己的新名称,而不是尝试重新定义__FILE__

【讨论】:

  • 混用预定义的宏不是一个好主意。由于它们的定义方式与其他宏不同,因此尝试更改它们将是特定于编译器的。
【解决方案3】:

考虑一下这个简单的源代码:

#include <stdio.h>
int main(void)
{
    puts(__FILE__);
    return(0);
}

在 Solaris 上,使用 GCC 4.3.1,如果我使用以下代码进行编译:

gcc -o x x.c && ./x

输出是'x.c' 如果我编译它使用:

gcc -o x $PWD/x.c && ./x

然后 __FILE__ 映射到完整路径 ('/work1/jleffler/tmp/x.c')。如果我使用以下方法编译它:

gcc -o x ../tmp/x.c && ./x

然后 __FILE__ 映射到“../tmp/x.c”。

所以,基本上,__FILE__ 是源文件的路径名。如果您使用您希望在对象中看到的名称进行构建,那么一切都很好。

如果这是不可能的(无论出于何种原因),那么您将不得不接受其他人建议的修复。

【讨论】:

    【解决方案4】:

    既然您标记了 CMake,这里有一个巧妙的解决方案可以添加到您的 CMakeLists.txt: (复制自http://www.cmake.org/pipermail/cmake/2011-December/048281.html)。 (注意:一些编译器不支持 per-file COMPILE_DEFINITIONS !但它适用于 gcc)

    set(SRCS a/a.cpp b/b.cpp c/c.cpp d/d.cpp)
    
    foreach(f IN LISTS SRCS)
     get_filename_component(b ${f} NAME)
     set_source_files_properties(${f} PROPERTIES
      COMPILE_DEFINITIONS "MYSRCNAME=${b}")
    endforeach()
    
    add_executable(foo ${SRCS})
    

    注意:对于我的应用程序,我需要像这样转义文件名字符串:

    COMPILE_DEFINITIONS "MYSRCNAME=\"${b}\"")
    

    【讨论】:

      【解决方案5】:

      您的错误记录宏有什么作用?我会假设在某些时候宏最终会调用某种函数来进行日志记录,为什么不在运行时让被调用的函数剥离路径组件?

      #define LOG(message) _log(__FILE__, message)
      
      void _log(file, message)
      {
        #ifndef DEBUG
        strippath(file); // in some suitable way
        #endif
      
        cerr << "Log: " << file << ": " << message; // or whatever
      }
      

      【讨论】:

      • 这会阻止完整的文件名输出到日志,但仍将其包含在可执行文件中。我想这就是提问者所追求的。不过,这是一个很好的解决方案,所以没有反对意见。
      • 阅读这篇关于下划线的文章:stackoverflow.com/questions/228783/…
      • 我更希望有一个编译时解决方案。我认为在最终的可执行文件中有开发路径是草率的。
      • 注意每次调用日志函数时剥离字符串的开销...
      【解决方案6】:

      您也许可以使用模板元编程来做到这一点,但没有内置的方法来做到这一点。

      编辑:嗯,更正。根据one page I just saw,GCC 使用它为文件提供的路径。如果给定了全名,它将嵌入它;如果它只给出一个相对的,它只会嵌入那个。不过我自己没试过。

      【讨论】:

      • 我考虑过元编程,但我什至没有成功将 FILE 作为模板参数传递。
      • 我正在研究如何通过元编程来做到这一点,主要是为了我自己的个人启蒙。看起来应该是可能的。如果/当我想出解决方案时,我会编辑我的答案。
      【解决方案7】:

      借鉴 Glomek 的想法,它可以像这样自动化:

      源文件 x.c

      #line 1 MY_FILE_NAME
      #include <stdio.h>
      
      int main(void)
      {
          puts(__FILE__);
          return(0);
      }
      

      编译行(注意双引号外的单引号):

      gcc -DMY_FILE_NAME='"abcd.c"' -o x x.c
      

      输出是'abcd.c'。

      【讨论】:

        【解决方案8】:

        您可以将__FILE__ 分配给一个字符串,然后调用 _splitpath() 将其中的部分撕掉。这可能是一个仅限 Windows/MSVC 的解决方案,老实说我不知道​​。

        我知道您正在寻找一个编译时解决方案,这是一个运行时解决方案,但我认为由于您使用文件名来执行(可能是运行时)错误记录,这可能是一种简单直接的方法为您提供所需的东西。

        【讨论】:

        • 这是个好主意,但已经有人建议过两次。请尽可能为现有解决方案投票。
        • 没有其他人建议使用 _splitpath()。我发现很多人不知道存在这样的函数,最终他们自己解析字符串。我的解决方案将保留。
        【解决方案9】:

        使用 cmake 很容易。

        DefineRelativeFilePaths.cmake

        function (cmake_define_relative_file_paths SOURCES)
          foreach (SOURCE IN LISTS SOURCES)
            file (
              RELATIVE_PATH RELATIVE_SOURCE_PATH
              ${PROJECT_SOURCE_DIR} ${SOURCE}
            )
        
            set_source_files_properties (
              ${SOURCE} PROPERTIES
              COMPILE_DEFINITIONS __RELATIVE_FILE_PATH__="${RELATIVE_SOURCE_PATH}"
            )
          endforeach ()
        endfunction ()
        

        CMakeLists.txt某处

        set (SOURCES ${SOURCES}
          "${CMAKE_CURRENT_SOURCE_DIR}/common.c"
          "${CMAKE_CURRENT_SOURCE_DIR}/main.c"
        )
        
        include (DefineRelativeFilePaths)
        cmake_define_relative_file_paths ("${SOURCES}")
        

        cmake .. &amp;&amp; make clean &amp;&amp; make VERBOSE=1

        cc ... -D__RELATIVE_FILE_PATH__="src/main.c" ... -c src/main.c
        

        就是这样。现在你可以制作漂亮的日志消息了。

        #define ..._LOG_HEADER(target) \
          fprintf(target, "%s %s:%u - ", __func__, __RELATIVE_FILE_PATH__, __LINE__);
        

        func src/main.c:22 - 我的错误

        PS 最好在config.h.in->config.hdeclear

        #ifndef __RELATIVE_FILE_PATH__
        #define __RELATIVE_FILE_PATH__ __FILE__
        #endif
        

        所以你的 linter 不会提供大量错误。

        【讨论】:

          【解决方案10】:

          这个问题已经有 18 年了,早在 2008 年这个解决方案还不可用,但是

          从 GCC 8 和 CLANG 10 开始,可以使用选项 -fmacro-prefix-map。
          根据 GCC 手册:

          -fmacro-prefix-map=old=new
          预处理位于目录 ‘old’ 中的文件时,展开 __FILE____BASE_FILE__ 宏就像文件驻留在 目录 'new' 代替。这可用于将绝对路径更改为 使用 '.' 作为 new 的相对路径,这可能会导致更多 与位置无关的可重现构建。这个选项也 在编译期间影响__builtin_FILE()。也可以看看 ‘-ffile-prefix-map’。

          例如,我的 IDE (Eclipse) 中的 makefile 为某些文件包含以下 GCC 参数:-fmacro-prefix-map="../Sources/"=.
          因此,我的调试日志总是只显示文件名,不显示路径。

          注意:GCC 8.1 和 Clang 10 分别于 2018 年 5 月和 2020 年 3 月发布。因此,目前,在 2020 年 9 月,我只有部分环境支持 -fmacro-prefix-map。

          【讨论】:

            【解决方案11】:

            您可以使用 __FILE__ 并去掉您不想要的路径部分(以编程方式)。如果 basedir 满足您的需求,那很好。否则,从您的构建系统获取源目录根目录,其余的应该是可行的。

            【讨论】:

              【解决方案12】:

              刚刚遇到同样的问题;找到了一个不同的解决方案,只是想我会分享它:

              在我所有其他文件中包含的头文件中:

              static char * file_bname = NULL;
              #define __STRIPPED_FILE__   (file_bname ?: (file_bname = basename(__FILE__)))
              

              希望这对其他人也有用:)

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-04-17
                • 2020-02-14
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多