同时使用gcc和-std = c11以及g ++与-std = c ++ 14。
E.g。对于名为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:
#include <Joe/Logger.h>
,并且在稍后的调用中,例如LOG_DEBUG(...)
我将隐含地使用自动生成的&#34;通用区域标识符&#34;。JOE_LOG_FILE_REGION(Hello);
(#include <Joe/Logger.h>
之后)才能将LOG_DEBUG(...)
放入其代码中。答案 0 :(得分:17)
#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
或
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
示例:
#include <stdio.h>
constexpr const char* str_end(const char *str) {
return *str ? str_end(str + 1) : str;
}
constexpr bool str_slant(const char *str) {
return *str == '/' ? true : (*str ? str_slant(str + 1) : false);
}
constexpr const char* r_slant(const char* str) {
return *str == '/' ? (str + 1) : r_slant(str - 1);
}
constexpr const char* file_name(const char* str) {
return str_slant(str) ? r_slant(str_end(str)) : str;
}
int main() {
constexpr const char *const_file = file_name(__FILE__);
puts(const_file);
return 0;
}
源文件名为foo/foo1/foo2/foo3/foo4.cpp
使用g++ -o foo.exe foo/foo1/foo2/foo3/foo4.cpp -std=c++11 --save-temps
编译此文件。
你可以看到这一点。
.file "foo4.cpp"
.section .rodata
.LC0:
.string "foo/foo1/foo2/foo3/foo4.cpp"
.text
.globl main
.type main, @function
main:
.LFB4:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq $.LC0+19, -8(%rbp)
movl $.LC0+19, %edi
call puts
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE4:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
.section .note.GNU-stack,"",@progbits
movl $.LC0+19, %edi
.LC0 + 19是没有路径和后缀
答案 1 :(得分:7)
如果从源文件所在的文件夹运行gcc,则会获得与传递绝对路径(即通过IDE传递给gcc)不同的__FILE__
。
gcc test.c -otest.exe
将__FILE__
作为test.c
。 gcc c:\tmp\test.c -otest.exe
将__FILE__
作为c:\tmp\test.c
。也许从源所在的路径调用gcc足以解决问题?
修改强>
这是一个“脏”但安全的黑客,它在编译时删除了文件扩展名。不是我推荐的东西,但写起来很有趣:)所以把它当作值得的东西。它只适用于C。
#include <stdio.h>
#define EXT_LENGTH (sizeof(".c") - 1) // -1 null term
typedef union
{
char filename_no_nul [sizeof(__FILE__)-EXT_LENGTH-1]; // -1 null term
char filename_nul [sizeof(__FILE__)-EXT_LENGTH];
} remove_ext_t;
int main (void)
{
const remove_ext_t file = { __FILE__ };
puts(file.filename_nul);
return 0;
}
union分配一个足够大的成员来保存完整路径减去扩展名和null终止符。并且它分配了一个足够大的成员来保存完整路径减去扩展名,但是使用空终止符。
太小而无法容纳完整__FILE__
的成员会使用尽可能多的__FILE__
进行初始化。这在C中是可以的,但在C ++中是不允许的。如果__FILE__
包含test.c
,则联合成员现在将被初始化为包含test
且没有空终止符。
但是在该字符串之后仍会有尾随零,因为这个hack滥用了另一个union成员已根据“aggregate / union”初始化规则初始化的事实。该规则强制“聚合”中的任何剩余项目被初始化,就好像它们具有静态存储持续时间,即为零。这恰好是null终止符的值。
答案 2 :(得分:6)
在编译时提取基本文件名,没有预处理器技巧,也没有外部脚本? C ++ 14?没问题,先生。
#include <iostream>
#include <string>
using namespace std;
namespace detail {
constexpr bool is_path_sep(char c) {
return c == '/' || c == '\\';
}
constexpr const char* strip_path(const char* path)
{
auto lastname = path;
for (auto p = path ; *p ; ++p) {
if (is_path_sep(*p) && *(p+1)) lastname = p+1;
}
return lastname;
}
struct basename_impl
{
constexpr basename_impl(const char* begin, const char* end)
: _begin(begin), _end(end)
{}
void write(std::ostream& os) const {
os.write(_begin, _end - _begin);
}
std::string as_string() const {
return std::string(_begin, _end);
}
const char* const _begin;
const char* const _end;
};
inline std::ostream& operator<<(std::ostream& os, const basename_impl& bi) {
bi.write(os);
return os;
}
inline std::string to_string(const basename_impl& bi) {
return bi.as_string();
}
constexpr const char* last_dot_of(const char* p) {
const char* last_dot = nullptr;
for ( ; *p ; ++p) {
if (*p == '.')
last_dot = p;
}
return last_dot ? last_dot : p;
}
}
// the filename with extension but no path
constexpr auto filename = detail::strip_path(__FILE__);
constexpr auto basename = detail::basename_impl(filename, detail::last_dot_of(filename));
auto main() -> int
{
cout << filename << endl;
cout << basename << endl;
cout << to_string(basename) << endl;
return 0;
}
答案 3 :(得分:3)
结果很简单,你只需要#line
preprocessor directive,例如
#line 0 "Hello"
在文件的顶部,按原样,如果您想要完全隐藏文件名,那么
#line 0 ""
会起作用。
如果您不想使用Makefile
,则可以使用此
file=cfile;
content=$(sed -e "1s/^/#line 0 \"$file\"\n/" example/${file}.c);
echo $content | gcc -xc -O3 -o ${file} -
上面的-xc
gcc标志表示(来自gcc&#39; s文档):
-x
语言:明确指定以下输入文件的语言(而不是让编译器根据文件名后缀选择默认值)。此选项适用于所有后续输入文件,直到下一个-x选项。语言的可能值为:
c c-header cpp-output c++ c++-header c++-cpp-output objective-c objective-c-header objective-c-cpp-output objective-c++ objective-c++-header objective-c++-cpp-output assembler assembler-with-cpp ada f77 f77-cpp-input f95 f95-cpp-input go java
如果您没有任何可以帮助您构建源代码的脚本,那么我认为没有办法实现。
此外,您可以从gcc文档的上述引文中看到,您可以保存文件而不需要任何扩展名,然后合并@ Lundin&#39; s 原始使用此解决方案并使用
gcc -xc -o file filename_without_extension
在这种情况下__FILE__
会扩展为"filename_without_extension"
,你可以实现你想要的,虽然你需要在它所在的同一目录中编译该文件,否则它将包含路径到文件。