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.
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 $<))\"'")
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.
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 ?le (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.
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.
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.
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;
}
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__);
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.
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
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
: persists 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__))]);
}
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.
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
.
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.
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:
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.
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__
.
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...
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.
#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
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__
.
Recent Clang compiler has a __FILE_NAME__
macro (see here).
A short, working answer for both Windows and *nix:
#define __FILENAME__ std::max<const char*>(__FILE__,\
std::max(strrchr(__FILE__, '\\')+1, strrchr(__FILE__, '/')+1))
In VC, when using /FC
, __FILE__
expands to the full path, without the /FC
option __FILE__
expands file name. ref: here
Source: Stackoverflow.com