同时使用gcc和-std=c11以及g和-std=c14。
例如,对于一个名为src/dir/Hello.cxx
的文件,它应该扩展为如下内容:
const char basename[] = "Hello";
或
const char basename[] = getStaticBasename(__FILE__);
其中getStaticBasename()
是一个宏(对于C源代码)或constexpr函数(对于C++源代码),其结果为“Hello”。
我必须避免在运行时从__FILE__
拆分字符串,因为路径和后缀不能以任何方式编译到可执行文件中。
解决方案必须不依赖于boost等大型库。
因为我没有makefile,所以像this这样的解决方案不能在我的情况下使用。
有办法解决吗?
编辑2015-07-02:
- 我对编译器和链接器的调用方式没有影响(有时通过makefile,有时从命令行,或一些IDE(Eclipse CDT管理的make,Crossworks,Xcode等)。因此,解决方案只需要在代码中。
- 我的用例是为一个小型日志记录解决方案提供某种“通用区域标识符”。应用程序代码(使用我的日志记录器)应该只包含
#include <Joe/Logger.h>
,并且在后面对例如LOG_DEBUG(...)
我将隐式地使用自动生成的“通用区域标识符”。 - 我目前的解决方案是,应用程序代码必须声明一个
JOE_LOG_FILE_REGION(Hello);
(在#include <Joe/Logger.h>
之后),然后才能将LOG_DEBUG(...)
放入其代码中。
6条答案
按热度按时间mwecs4sa1#
1. gcc内置函数可以在编译时获取全路径的文件名。
#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
或
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
2. c++11 constexpr也可以在编译时执行此操作。
c++11 constexpr函数只能使用return-statement。
示例:
源文件名为
foo/foo1/foo2/foo3/foo4.cpp
使用
g++ -o foo.exe foo/foo1/foo2/foo3/foo4.cpp -std=c++11 --save-temps
编译此文件。你可以看到这个
movl $.LC0+19, %edi
.LC0 + 19是不带路径和后缀的文件名字符串的地址3. c++14 constexpr函数可以用一种简单的方法做到这一点
c++14 constexpr函数可以使用循环和局部变量。
file_name
函数将在编译时替换为const char *
地址。~7rfyedvj2#
在编译时提取基本文件名,而不需要预处理器技巧和外部脚本?#21440;,没有问题。
cgyqldqp3#
如果从源文件所在的文件夹中运行gcc,您将得到一个与传递绝对路径不同的
__FILE__
(即通过IDE交给gcc)。gcc test.c -otest.exe
给出了__FILE__
作为test.c
。gcc c:\tmp\test.c -otest.exe
给出了__FILE__
作为c:\tmp\test.c
。也许从源所在的路径调用gcc就足够了?
编辑
这里有一个“肮脏”但安全的黑客,它在编译时删除了文件扩展名。不是我推荐的东西,但写起来很有趣:)所以就拿它来说吧。它只在C中工作。
联合体分配一个足够大的成员,以容纳完整路径减去扩展和空终止符。它分配一个足够大的成员,以容纳完整的路径减去扩展,尽管有一个空终止符。
如果成员太小,无法容纳完整的
__FILE__
,则使用尽可能多的__FILE__
进行初始化。这在C中是可以的,但在C++中是不允许的。如果__FILE__
包含test.c
,联合成员现在将被初始化为包含test
,没有空终止符。然而,在该字符串之后仍然会有尾随的零,因为这种黑客滥用了另一个联合成员已经根据“聚合/联合”初始化规则初始化的事实。该规则强制“聚合”中的任何剩余项被初始化,就好像它们具有静态存储持续时间,即初始化为零。它恰好是空终止符的值。
xuo3flqw4#
结果非常简单,只需要
#line
预处理器指令,例如在文件的顶部,这是,如果所有你想要的是完全隐藏文件名,然后
会有用的
如果不想使用
Makefile
s,可以使用以下命令上面的
-xc
gcc标志意味着(来自gcc的文档):-x
:明确指定下列输入文件的语言(而不是让编译器根据文件名后缀选择默认语言)。此选项适用于以下所有输入文件,直到下一个-x选项。语言的可能值为:
如果你没有任何类型的脚本来帮助你构建源代码,那么我认为就没有办法做到这一点。
另外,从上面引用的gcc文档中可以看出,您可以完全不带任何扩展名地保存文件,然后将@Lundin的 original 解决方案与此结合使用。
在这种情况下,
__FILE__
将扩展为"filename_without_extension"
,您将实现您想要的,尽管您需要在它所在的同一目录中编译文件,因为否则它将包含文件的路径。628mspwn5#
投票最多的解决方案不依赖于回答OP,因为完整的文件路径存储在二进制文件中,并且仅计算和使用指向路径最后部分的指针(从最后一个'/'字符开始)。
请参见@pexeer answer中建议的解决方案的汇编输出:
为了避免存储完整的文件路径,您需要类似于以下内容:
你会得到这个程序集输出(其中没有存储完整的文件路径):
示例here
这里的基本思想是构造一个静态初始化的char数组,其中只包含所需的字符串(而不是指向包含完整文件路径的静态char数组的指针)。推导文件长度是很简单的,但却是必需的,因为我们不能在 constexpr 函数中调用
strlen
。然后,技巧是使用整数序列作为文件指针数组中的索引(类似于自然声明:
const char f[] = {"str"[0], "str"[1], ...}
)。整数序列可用于可变参数模板示例化,因此必须在此类上下文中调用它。GCC将
print_impl
函数作为符号泄漏(因此它可能比文件的完整路径更大),但它可以在链接器步骤(或使用strip --strip-all /path/to/binary
)中剥离bq8i3lrv6#
不幸的是,似乎每个人都在忙碌着通过代码中的各种神奇方法删除路径中不需要的部分(-->大多数都不起作用)。
在我看来,正确的方法是告诉编译器从宏中更改/删除路径,避免所有修补的需要。使用gcc时,参数名为fmacro-prefix-map。你可以这样使用它:
将“/path/to/source/main.cpp”更改为“main.cpp”
顺便说一下:这也适用于std::source_location,当然完整路径(未更改)不会存储在结果二进制文件中。