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
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
Try
#include <string.h>
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
For Windows use '\\' instead of '/'.
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 $<))\"'")
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 $<))\"'"
where $(SOURCE_PREFIX)
is the prefix that you want to remove.
Then use __FILENAME__
in place of __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__
.
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
.
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;
}
'
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__))]);
}
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.
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...
Recent Clang compiler has a __FILE_NAME__
macro (see here).
In VC, when using /FC
, __FILE__
expands to the full path, without the /FC
option __FILE__
expands file name. ref: here
I have use the same solution with @Patrick 's answer for years.
It has a small issue when the full path contains symbol-link.
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 $<))\"'")
-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 makefile
s 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.
-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.
For Visual Studio, you can use the /d1trimfile
option.
You might want to set it to /d1trimfile:"$(SolutionDir)\"
:
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.
This behaviour has been implemented in some compilers, you can get basename of the compiled file by using:
__FILE_NAME__
(since GCC 12)__FILE_NAME__
(since CLANG 9.0.0)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 $<))\"'")
Here is the solution 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__);
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.
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.
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;
}
A short, working answer for both Windows and *nix:
#define __FILENAME__ std::max<const char*>(__FILE__,\
std::max(strrchr(__FILE__, '\\')+1, strrchr(__FILE__, '/')+1))
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.
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.
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.
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:
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
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
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.
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.
#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
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.
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__, '/')
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
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.
Since you are using GCC, you can take advantage of
__BASE_FILE__
This macro expands to the name of the main input file, in the form of a C string constant. This is the source file that was specified on the command line of the preprocessor or C compiler
and then control how you want to display the filename by changing the source file representation (full path/relative path/basename) at compilation time.