__FILE__ macro shows full path

CFileMacrosPath

C Problem Overview


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

/full/path/to/file.c

I see

to/file.c

or

file.c

C Solutions


Solution 1 - C

Try

#include <string.h>

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

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

Solution 2 - C

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__.

Solution 3 - C

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.

Solution 4 - C

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__.

Solution 5 - C

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.

Solution 6 - C

  • 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: 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__))]);
    }

https://godbolt.org/z/u6s8j3

Solution 7 - C

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...

Solution 8 - C

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

Solution 9 - C

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 $<))\"'")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")

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.

Solution 10 - C

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

Solution 11 - 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.

Solution 12 - C

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.

Solution 13 - C

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 $<))\"'")

Solution 14 - C

GCC 12 will provide GNU C extensions macro __FILE_NAME__ to get basename of the compiled file.

See GCC documentation which already contains this macro : gcc-common_predefined_macros

GCC thread : Bug 42579 - [PATCH] support for obtaining file basename

Solution 15 - C

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.

Solution 16 - C

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

Solution 17 - C

For Visual Studio, you can use the /d1trimfile option.

You might want to set it to /d1trimfile:"$(SolutionDir)\":

magic

Solution 18 - C

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.

Solution 19 - C

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.

Solution 20 - C

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__);

Solution 21 - C

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

Solution 22 - C

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.

Solution 23 - C

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: 

Solution 24 - C

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

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

Solution 25 - C

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.

Solution 26 - C

#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

Solution 27 - C

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.

Solution 28 - C

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__, '/')

Solution 29 - C

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

Solution 30 - 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmahmoodView Question on Stackoverflow
Solution 1 - Cred1ynxView Answer on Stackoverflow
Solution 2 - CPatrickView Answer on Stackoverflow
Solution 3 - CRenatoUtschView Answer on Stackoverflow
Solution 4 - Csimon.wattsView Answer on Stackoverflow
Solution 5 - CKeith ThompsonView Answer on Stackoverflow
Solution 6 - CAndryView Answer on Stackoverflow
Solution 7 - CSeva AlekseyevView Answer on Stackoverflow
Solution 8 - Cuser7382523View Answer on Stackoverflow
Solution 9 - CLevi.GView Answer on Stackoverflow
Solution 10 - Cstrange-cornerView Answer on Stackoverflow
Solution 11 - CSeanView Answer on Stackoverflow
Solution 12 - CProf. FalkenView Answer on Stackoverflow
Solution 13 - CGerneralAmericaView Answer on Stackoverflow
Solution 14 - CCharlie LegerView Answer on Stackoverflow
Solution 15 - ChmjdView Answer on Stackoverflow
Solution 16 - CKunisView Answer on Stackoverflow
Solution 17 - CPaulView Answer on Stackoverflow
Solution 18 - Calexander golksView Answer on Stackoverflow
Solution 19 - CAlain TotouomView Answer on Stackoverflow
Solution 20 - CDmitry GordonView Answer on Stackoverflow
Solution 21 - CIvanRView Answer on Stackoverflow
Solution 22 - Cbeavis9kView Answer on Stackoverflow
Solution 23 - CСаша ЗезюлинскийView Answer on Stackoverflow
Solution 24 - CLuc BloomView Answer on Stackoverflow
Solution 25 - CziuView Answer on Stackoverflow
Solution 26 - CDimfredView Answer on Stackoverflow
Solution 27 - CEvan Perry GroveView Answer on Stackoverflow
Solution 28 - CAlbert KView Answer on Stackoverflow
Solution 29 - CsergheiView Answer on Stackoverflow
Solution 30 - CCem PolatView Answer on Stackoverflow