__FILE__ 宏显示完整路径

发布于 2024-12-21 04:54:51 字数 214 浏览 3 评论 0 原文

C 中可用的标准预定义宏 __FILE__ 显示文件的完整路径。有什么方法可以缩短路径并仅获取文件名吗?我的意思是,而不是

/full/path/to/file.c

我看到

to/file.c

file.c

The standard predefined macro __FILE__ available in C shows the full path to the file. Is there any way to shorten the path and get just the filename? I mean instead of

/full/path/to/file.c

I see

to/file.c

or

file.c

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

止于盛夏 2024-12-28 04:54:52

使用 basename() 函数,或者,如果您使用的是 Windows,_splitpath()

#include <libgen.h>

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

还可以在 shell 中尝试 man 3 basename

Use the basename() function, or, if you are on Windows, _splitpath().

#include <libgen.h>

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

Also try man 3 basename in a shell.

彩扇题诗 2024-12-28 04:54:52

如果您将 CMAKE 与 GNU 编译器一起使用,则此 global 定义可以正常工作:

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

If you are using CMAKE with GNU compiler this global define works fine:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__='\"$(notdir $(abspath 
lt;))\"'")
岁月如刀 2024-12-28 04:54:52

@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) 文件都会执行一次构造,而不是每次引用宏时执行一次。

它仅限于从 .c(pp) 文件使用,并且无法从头文件中使用。

A slight variation on what @red1ynx proposed would to be create the following macro:

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

In each of your .c(pp) files add:

SET_THIS_FILE_NAME();

Then you can refer to THIS_FILE_NAME instead of __FILE__:

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

This means the construction is performed once per .c(pp) file instead of each time the macro is referenced.

It is limited to use only from .c(pp) files and would be unusable from header files.

玩套路吗 2024-12-28 04:54:52

只是希望稍微改进一下 FILE 宏:

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

这会捕获 / 和 \,就像 Czarek Tomczak 所要求的那样,这在我的混合环境。

just hope to improve FILE macro a bit:

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

this catches / and \, like Czarek Tomczak requested, and this works great in my mixed environment.

做个ˇ局外人 2024-12-28 04:54:52

我做了一个宏 __FILENAME__ 以避免每次都剪切完整路径。问题是将生成的文件名保存在 cpp 局部变量中。

通过在 .h 文件中定义静态全局变量可以轻松完成此操作。
此定义在包含 .h 的每个 .cpp 文件中给出了单独且独立的变量。
为了成为多线程证明,值得使变量也成为线程本地(TLS)。

一个变量存储文件名(已缩小)。另一个保存 __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;
}

I did a macro __FILENAME__ that avoids cutting full path each time. The issue is to hold the resulting file name in a cpp-local variable.

It can be easily done by defining a static global variable in .h file.
This definition gives separate and independent variables in each .cpp file that includes the .h.
In order to be a multithreading-proof it worth to make the variable(s) also thread local (TLS).

One variable stores the File Name (shrunk). Another holds the non-cut value that __FILE__ gave. The h file:

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

The macro itself calls method with all the logic:

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

And the function is implemented this way:

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

The removePath() can be implemented in different ways, but the fast and simple seems to be with 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;
}
乙白 2024-12-28 04:54:52

尝试

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

在源文件中的 include 语句之后添加,并添加

#pragma pop_macro("__FILE__")

到源文件的末尾。

Try

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

after the include statements in your source file and add

#pragma pop_macro("__FILE__")

at the end of your source file.

柠檬 2024-12-28 04:54:52

这是一个可移植函数,适用于 Linux(路径“/”)和 Windows(“\”和“/”的混合)。
使用 gcc、clang 和

#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: 

Here is a portable function that works for both Linux (path '/') and Windows (mix of '\' and '/').
Compiles with gcc, clang and 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;
}

Standard output:

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: 
混吃等死 2024-12-28 04:54:52

如果您最终在此页面上寻找一种方法来从您正在发送的二进制文件中删除指向难看的构建位置的绝对源路径,那么下面的内容可能会满足您的需求。

虽然这并不能准确地给出作者所表达的愿望的答案,因为它假设使用 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

If you ended up on this page looking for a way to remove absolute source path that is pointing to ugly build location from the binary that you are shipping, below might suit your needs.

Although this doesn't produce exactly the answer that the author has expressed his wish for since it assumes the use of CMake, it gets pretty close. It's a pity this wasn't mentioned earlier by anyone as it would have saved me loads of time.

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

Setting above variable to ON will generate build command in the format:

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

As a result, __FILE__ macro will resolve to ../../src/path/to/source.c

CMake documentation

Beware of the warning on the documentation page though:

Use relative paths (May not work!).

It is not guaranteed to work in all cases, but worked in mine - CMake 3.13 + gcc 4.5

标点 2024-12-28 04:54:52

适用于 Windows 和 *nix 的简短有效答案:

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

A short, working answer for both Windows and *nix:

#define __FILENAME__ std::max<const char*>(__FILE__,\
    std::max(strrchr(__FILE__, '\\')+1, strrchr(__FILE__, '/')+1))
夜声 2024-12-28 04:54:52

在 MSVC 中,将 FILENAME 宏作为 FILENAME=%(FileName)%(Extension) 添加到 C++ 项目的预处理器定义中。恐怕这完全就是一个编译器杀手。不知怎的,它破坏了并行构建。

In MSVC, add FILENAME macro as FILENAME=%(FileName)%(Extension) to the Preprocessor Definitions of the C++ project. I'm afraid this is completely a compiler killer. Somehow it breaks parallel build.

东走西顾 2024-12-28 04:54:52

这是一个适用于没有字符串库的环境(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__。是的,这仍然是一个运行时的事情,但它确实有效。

Here's a solution that works for environments that don't have the string library (Linux kernel, embedded systems, etc):

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

Now just use FILENAME instead of __FILENAME__. Yes, it's still a runtime thing but it works.

仙女 2024-12-28 04:54:52

red1ynx 的答案经过调整,甚至更加“臃肿”:

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

如果我们找到反斜杠,我们就会根据反斜杠进行拆分。否则,按正斜杠分割。够简单的。

几乎任何替代方案都会更干净(在我看来,C++ constexpr 实际上是这里的黄金标准)。但是,如果您使用的某些编译器中 __BASE_FILE__ 不可用,这可能会有所帮助。

A tweaked, even more "bloated" version of red1ynx's answer:

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

If we find backslashes, we split on backslashes. Otherwise, split on forward slash. Simple enough.

Just about any alternative would be cleaner (A C++ constexpr is really the gold standard here, in my opinion). However, this may be helpful if you're using some compiler where __BASE_FILE__ isn't available.

倾其所爱 2024-12-28 04:54:52

我认为这比使用 strrchr 函数更好。
strfnchr 将搜索最后一个 delemeter '/' 并从 __FILE__ 获取文件名
您可以使用 __FILE__NAME__ 代替 __FILE__ 来获取没有完整文件路径的文件名。
strrchr 解决方案每次使用都会搜索文件名两次。但这段代码只是搜索1次。
即使 __FILE__ 中没有分隔符“/”,它也能有效地工作。
您可以根据需要将其替换为 \ 来使用它。
使用下面的strrchr源代码改进了strfnchr源代码。我认为它会比 strrchr 更有效。
https://code.woboq.org/userspace/glibc/string/ strrchr.c.html

inline const char* strfnchr(const char* s, int c) {
  const char* found = s;
  while (*(s++)) {
    if (*s == c)
      found = s;
  }
  if (found != s)
    return found + 1;
  return s;
}

#define __FILE_NAME__ strfnchr(__FILE__, '/')

I think this is better than using strrchr function.
strfnchr will search last delemeter '/' and get filename from __FILE__
and you can use __FILE__NAME__ instead __FILE__ for get file name without full file path.
strrchr solution searching filename twice per use. but this code is just 1 time search.
And it works effectively even if there is no seperater '/' in __FILE__.
You can use it by replacing it with \ as needed.
The source code of strfnchr was improved by using the source code of strrchr below. I think it will work more effectively than strrchr.
https://code.woboq.org/userspace/glibc/string/strrchr.c.html

inline const char* strfnchr(const char* s, int c) {
  const char* found = s;
  while (*(s++)) {
    if (*s == c)
      found = s;
  }
  if (found != s)
    return found + 1;
  return s;
}

#define __FILE_NAME__ strfnchr(__FILE__, '/')
迷乱花海 2024-12-28 04:54:52

最上面的答案不够好,因为它不是编译时 const 表达式
这是一个非常简单的解决方案:

 #define FILESTEM(x)                                                        \
  std::string_view(x).substr(std::string_view(x).rfind(OS_PATH_SLASH) + 1, \
                             std::string_view(x).rfind('.') -              \
                                 std::string_view(x).rfind(OS_PATH_SLASH) - 1)

它是一个 constexpr,可以在头文件中使用。

The top answer is not good enough for it's not a compile-time const expression
Here is a really simple solution:

 #define FILESTEM(x)                                                        \
  std::string_view(x).substr(std::string_view(x).rfind(OS_PATH_SLASH) + 1, \
                             std::string_view(x).rfind('.') -              \
                                 std::string_view(x).rfind(OS_PATH_SLASH) - 1)

it's a constexpr and can use in header file.

自此以后,行同陌路 2024-12-28 04:54:52
#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
#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
π浅易 2024-12-28 04:54:52

该解决方案基于 @RenatoUtsch 答案:

CMake list:

string(LENGTH "${PROJECT_SOURCE_DIR}/" SOURCE_PATH_SIZE)
add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")
add_definitions("-DSOURCE_PATH=\"${PROJECT_SOURCE_DIR}\"")

C/C++ header

#define __FILENAME__ strstr(__FILE__, SOURCE_PATH) + SOURCE_PATH_SIZE

This solution is based on @RenatoUtsch answer:

CMake list:

string(LENGTH "${PROJECT_SOURCE_DIR}/" SOURCE_PATH_SIZE)
add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")
add_definitions("-DSOURCE_PATH=\"${PROJECT_SOURCE_DIR}\"")

C/C++ header

#define __FILENAME__ strstr(__FILE__, SOURCE_PATH) + SOURCE_PATH_SIZE
辞旧 2024-12-28 04:54:51

对于 Windows,请尝试

#include <string.h>

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

使用“\\”而不是“/”。

Try

#include <string.h>

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

For Windows use '\\' instead of '/'.

痴者 2024-12-28 04:54:51

如果您使用 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 
lt;))\"'")

如果您使用 GNU make,我认为您没有理由不能扩展此内容到你自己的 makefile。例如,您可能有这样的行:

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

其中 $(SOURCE_PREFIX) 是您要删除的前缀。

然后使用 __FILENAME__ 代替 __FILE__

Here's a tip if you're using cmake. From:
http://public.kitware.com/pipermail/cmake/2013-January/053117.html

I'm copying the tip so it's all on this page:

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

If you're using GNU make, I see no reason you couldn't extend this to your own makefiles. For example, you might have a line like this:

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

where $(SOURCE_PREFIX) is the prefix that you want to remove.

Then use __FILENAME__ in place of __FILE__.

瑕疵 2024-12-28 04:54:51

GCC 8 现在具有 -fmacro-prefix-map-ffile-prefix-map 选项:

-fmacro-prefix-map==

当预处理驻留在目录old中的文件时,展开__FILE____BASE_FILE__宏,就像文件驻留在目录new<中一样/em> 相反。这可用于通过使用 . for new 将绝对路径更改为相对路径,这可以导致与位置无关的更可重现的构建。此选项还会影响编译期间的__builtin_FILE()。另请参阅-ffile-prefix-map


-ffile-prefix-map==

编译位于old目录中的文件时,记录所有引用
在编译结果中对它们进行处理,就好像文件驻留在
目录new代替。指定该选项相当于
指定所有单独的 -f*-prefix-map 选项。这个可以用
进行与位置无关的可重复构建。参见
-fmacro-prefix-map-fdebug-prefix-map

-ffile-prefix-map (-fdebug-prefix-map) 设置无效路径将中断调试,除非您告诉调试器如何映射回来。 (gdb:设置替换路径,vscode:“sourceFileMap”)。

如果您的目的只是清理__FILE__,则只需使用-fmacro-prefix-map

例子:
因此,对于我的 Jenkins 版本,我将添加 -ffile-prefix-map=${WORKSPACE}/=/ 和另一个以删除本地开发包安装前缀。

注意 不幸的是,-ffile-prefix-map-fmacro-prefix-map 选项仅在 GCC 8 及以后版本中可用。例如,对于 GCC 5,我们只有 -fdebug-prefix-map,它不会影响 __FILE__

GCC 8 now has the -fmacro-prefix-map and -ffile-prefix-map options:

-fmacro-prefix-map=old=new

When preprocessing files residing in directory old, expand the __FILE__ and __BASE_FILE__ macros as if the files resided in directory new instead. This can be used to change an absolute path to a relative path by using . for new which can result in more reproducible builds that are location independent. This option also affects __builtin_FILE() during compilation. See also -ffile-prefix-map.

-ffile-prefix-map=old=new

When compiling files residing in directory old, record any references
to them in the result of the compilation as if the files resided in
directory new instead. Specifying this option is equivalent to
specifying all the individual -f*-prefix-map options. This can be used
to make reproducible builds that are location independent. See also
-fmacro-prefix-map and -fdebug-prefix-map.

Setting an invalid path for -ffile-prefix-map (-fdebug-prefix-map) will break debugging unless you tell your debugger how to map back. (gdb: set substitue-path, vscode: "sourceFileMap").

If your intent is to only clean up __FILE__ just use -fmacro-prefix-map.

Example:
So for my Jenkins builds I will add -ffile-prefix-map=${WORKSPACE}/=/, and another to remove the local dev package install prefix.

NOTE Unfortunately the -ffile-prefix-map and -fmacro-prefix-map options are only available in GCC 8 onwards. For, say, GCC 5, we only have -fdebug-prefix-map which does not affect __FILE__.

请别遗忘我 2024-12-28 04:54:51

我刚刚想到了一个很好的解决方案,它可以与源文件和头文件一起使用,非常高效,并且可以在所有平台上的编译时工作,而无需特定于编译器的扩展。此解决方案还保留项目的相对目录结构,因此您知道文件位于哪个文件夹中,并且仅相对于项目的根目录。

这个想法是使用构建工具获取源目录的大小,然后将其添加到 __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 创建另一个宏。

I have just thought of a great solution to this that works with both source and header files, is very efficient and works on compile time in all platforms without compiler-specific extensions. This solution also preserves the relative directory structure of your project, so you know in which folder the file is in, and only relative to the root of your project.

The idea is to get the size of the source directory with your build tool and just add it to the __FILE__ macro, removing the directory entirely and only showing the file name starting at your source directory.

The following example is implemented using CMake, but there's no reason it wouldn't work with any other build tools, because the trick is very simple.

On the CMakeLists.txt file, define a macro that has the length of the path to your project on 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}")

On your source code, define a __FILENAME__ macro that just adds the source path size to the __FILE__ macro:

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)

Then just use this new macro instead of the __FILE__ macro. This works because the __FILE__ path will always start with the path to your CMake source dir. By removing it from the __FILE__ string the preprocessor will take care of specifying the correct file name and it will all be relative to the root of your CMake project.

If you care about the performance, this is as efficient as using __FILE__, because both __FILE__ and SOURCE_PATH_SIZE are known compile time constants, so it can be optimized away by the compiler.

The only place where this would fail is if you're using this on generated files and they're on a off-source build folder. Then you'll probably have to create another macro using the CMAKE_BUILD_DIR variable instead of CMAKE_SOURCE_DIR.

我不在是我 2024-12-28 04:54:51

'

    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", 文件);
    
  • 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

'

    int main()
    {
         printf("%s\n", &__FILE__[get_file_name_offset(__FILE__)]);
    }

Code generates a compile time offset when:

  • gcc: at least gcc6.1 + -O1

  • msvc: put result into constexpr variable:

        constexpr auto file = &__FILE__[get_file_name_offset(__FILE__)];
        printf("%s\n", file);
    
  • clang: insists on not compile time evaluation

There is a trick to force all 3 compilers does compile time evaluation even in the debug configuration with disabled optimization:

    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

青瓷清茶倾城歌 2024-12-28 04:54:51

至少对于 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 来说,这也会影响诊断消息中报告的文件名。

At least for gcc, the value of __FILE__ is the file path as specified on the compiler's command line. If you compile file.c like this:

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

the __FILE__ will expand to "/full/path/to/file.c". If you instead do this:

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

then __FILE__ will expand to just "file.c".

This may or may not be practical.

The C standard does not require this behavior. All it says about __FILE__ is that it expands to "The presumed name of the current source file (a character string literal)".

An alternative is to use the #line directive. It overrides the current line number, and optionally the source file name. If you want to override the file name but leave the line number alone, use the __LINE__ macro.

For example, you can add this near the top of file.c:

#line __LINE__ "file.c"

The only problem with this is that it assigns the specified line number to the following line, and the first argument to #line has to be a digit-sequence so you can't do something like

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

Ensuring that the file name in the #line directive matches the actual name of the file is left as an exercise.

At least for gcc, this will also affect the file name reported in diagnostic messages.

君勿笑 2024-12-28 04:54:51

这里纯粹是编译时解决方案。它基于以下事实:字符串文字的 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__)

请随意将条件运算符级联扩展到项目中的最大合理文件名。路径长度并不重要,只要您检查距离字符串末尾足够远即可。

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

Purely compile time solution here. It's based on the fact that sizeof() of a string literal returns its 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__)

Feel free to extend the conditional operator cascade to the maximum sensible file name in the project. Path length doesn't matter, as long as you check far enough from the end of the string.

I'll see if I can get a similar macro with no hard-coded length with macro recursion...

难以启齿的温柔 2024-12-28 04:54:51

最近的 Clang 编译器有一个 __FILE_NAME__ 宏(请参阅此处)。

Recent Clang compiler has a __FILE_NAME__ macro (see here).

小兔几 2024-12-28 04:54:51

对于 Visual Studio,您可以使用(未​​记录/实验性)/d1trimfile 编译器选项。它采用不区分大小写的前缀参数,并且具有在计算此宏时从 __FILE__ 中删除此前缀的效果。

应用 /d1trimfile:"$(SolutionDir)\" 选项可有效地将所有 __FILE__ 计算更改为相对于解决方案目录:

magic

注意:关于 " 之前的尾随 \ 的说明:

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

For Visual Studio, you can use the (undocumented/experimental) /d1trimfile compiler option. It takes a case-insensitive prefix argument and has the effect of stripping this prefix from __FILE__ whenever this macro is evaluated.

Applying the /d1trimfile:"$(SolutionDir)\" option effectively changes all __FILE__ evaluations to become solution directory relative:

magic

NB: Explanation for the trailing \ right before the ":

To escape the first one ($(SolutionDir) ends in a backslash), otherwise the quote is escaped. You need an even amount of backslashes
before the quote.

单身情人 2024-12-28 04:54:51

多年来我一直使用与 @Patrick 的答案相同的解决方案。

当完整路径包含符号链接时,它有一个小问题。

更好的解决方案。

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath 
lt;))\"'")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath 
lt;))\"'")

为什么要使用这个?

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

    <块引用>

    对于那些不支持此功能的编译器,请参阅下面的稳健方法

  • 从文件路径中删除项目路径是您的真正要求。您不会愿意浪费时间去查找 header.h 文件、src/foo/header.hsrc/bar/header 在哪里.h.

  • 我们应该删除 cmake 配置文件中的 __FILE__ 宏。

    大多数现有代码中都使用该宏。只需重新定义它即可让您自由。

    gcc这样的编译器从命令行参数预定义了这个宏。完整路径写入由 cmake 生成的 makefile 中。

  • 需要 CMAKE_*_FLAGS 中的硬代码。

    在某些较新的版本中,有一些命令可以添加编译器选项或定义,例如 add_definitions()add_compile_definitions()。这些命令将在应用于源文件之前解析 make 函数,例如 subst。这不是我们想要的。

-Wno-builtin-macro-redefine 的稳健方法。

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__=...) 行中删除此编译器选项。

I have use the same solution with @Patrick 's answer for years.

It has a small issue when the full path contains symbol-link.

Better solution.

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath 
lt;))\"'")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath 
lt;))\"'")

Why should use this ?

  • -Wno-builtin-macro-redefined to mute the compiler warnings for redefining __FILE__ macro.

    For those compilers do not support this, refer to the Robust way below.

  • Strip the project path from the file path is your real requirement. You won't like to waste the time to find out where is a header.h file, src/foo/header.h or src/bar/header.h.

  • We should strip the __FILE__ macro in cmake config file.

    This macro is used in most exists codes. Simply redefine it can set you free.

    Compilers like gcc predefines this macro from the command line arguments. And the full path is written in makefiles generated by cmake.

  • Hard code in CMAKE_*_FLAGS is required.

    There is some commands to add compiler options or definitions in some more recently version, like add_definitions() and add_compile_definitions(). These commands will parse the make functions like subst before apply to source files. That is not we want.

Robust way for -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)

Remember to remove this compiler option from the set(*_FLAGS ... -D__FILE__=...) line.

傲世九天 2024-12-28 04:54:51

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

In VC, when using /FC, __FILE__ expands to the full path, without the /FC option __FILE__ expands file name. ref: here

仄言 2024-12-28 04:54:51

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

This behaviour has been implemented in some compilers, you can get basename of the compiled file by using:

筱武穆 2024-12-28 04:54:51

以下是 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__);

Here is the solution available with C++14 or newer that uses compile-time calculation:

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__);
拥抱影子 2024-12-28 04:54:51

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

There's no compile time way to do this. Obviously you can do it at runtime using the C runtime, as some of the other answers have demonstrated, but at compile time, when the pre-procesor kicks in, you're out of luck.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文