212

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
raving_sage
  • 13
  • 1
  • 5
mahmood
  • 23,197
  • 49
  • 147
  • 242
  • 32
    It would be really really great to find a preprocessor-only solution. I'm afraid that the suggestions based on string operations will execute at runtime. – cdleonard Dec 13 '11 at 11:26
  • 9
    Since you're using gcc, I think you can change what `__FILE__` contains by changing the filename you pass on the command line. So instead of `gcc /full/path/to/file.c`, try `cd /full/path/to; gcc file.c; cd -;`. Of course there's a bit more to it than that if you're relying on gcc's current directory for the include path or output file location. Edit: the gcc docs suggest that it's the full path, *not* the input file name argument, but that's not what I'm seeing for gcc 4.5.3 on Cygwin. So you may as well try it on Linux and see. – Steve Jessop Dec 13 '11 at 11:44
  • 4
    GCC 4.5.1 (built for arm-none-eabi specifically) uses the exact text of the file name on its command line. In my case it was the IDE's fault for invoking GCC with all file names fully qualified instead of putting the current directory somewhere sensible (location of the project file, perhaps?) or configurable and using relative paths from there. I suspect a lot of IDEs do that (especially on Windows) out of some sort of discomfort related to explaining where the "current" directory really is for a GUI app. – RBerteig Sep 13 '12 at 21:03
  • 3
    @SteveJessop - hope you read this comment. I have a situation where I see `__FILE__` printed as `../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.c` and I want to know where gcc compiled the file such that this file is relatively located like it is shown. – Chan Kim Sep 14 '14 at 04:32
  • 2
    This question is not a dupe of the linked one. For one, the linked one is about C++, and the answers consequently delves into C++ macro esoterica. Second, there is nothing in OP's question which mandates a macro solution. It only solemnly points out a problem and asks an open ended question. – Prof. Falken Jun 08 '15 at 17:53
  • I know this ask is years old, but this will be possible to do at compile time [in GCC 9](https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=256847) (I don't *think* that made it onto the 8 branch, but I could be wrong). – Daniel H Apr 19 '18 at 18:52
  • You could use the `basename()` function to extract just the current file name for printing – user3629249 Jan 23 '19 at 22:34
  • With at least the `C++11` you can do it at compile time instead of in the preprocessor: https://stackoverflow.com/questions/8487986/file-macro-shows-full-path/54335644#54335644 – Andry May 14 '19 at 07:27
  • FYI here is a 4 line `c++11` solution using compile-time `constexpr` tested with g++ version 4.8.5: https://stackoverflow.com/a/38237385/52074 – Trevor Boyd Smith May 14 '19 at 12:51

31 Answers31

213

Try

#include <string.h>

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

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

Jayesh
  • 51,787
  • 22
  • 76
  • 99
red1ynx
  • 3,639
  • 1
  • 18
  • 23
  • 1
    @mahmood: `strrchr` returns a pointer to the `/` (or a null pointer if it doesn't find a `/`), so `+1` means that the resulting pointer is one character further on. Without it, the result string would be `/file.c` instead of `file.c`. – Steve Jessop Dec 13 '11 at 11:34
  • 18
    `/` is a valid path separator in Windows. – Hans Passant Dec 13 '11 at 11:56
  • But I think all of path has to be with forward slashes. – Prof. Falken Dec 13 '11 at 12:21
  • 11
    `/` is a valid path separator in file names passed to CreateFile() and so forth. However, that doesn't always mean that you can use `/` just anywhere in Windows since there is a tradition (inherited from CPM) of using `/` as the argument lead in at the command prompt. But a quality tool would be careful to split file names at *both* slash and backslash characters to avoid any problems for folks that do manage to use `/`. – RBerteig Sep 13 '12 at 20:53
  • 6
    @AmigableClarkKant, no you can mix both separators in the same file name. – RBerteig Sep 13 '12 at 20:55
  • 1
    `__FILE__` may contain mixed separators, in my case I see `c:\phpdesktop\phpdesktop-src\phpdesktop-msie\msie/internet_features.h`. Anyone have a macro for that? – Czarek Tomczak Feb 06 '13 at 20:01
  • 2
    If your platform supports it `char* fileName = basename(__FILE__);` It's definitely there in Linux and OS X, don't know about Windows though. – JeremyP Jul 17 '13 at 12:47
  • I tried that with '\\' for windows and it didn't work , can you please help me see what I am missing http://stackoverflow.com/questions/27493937/base-file-name-from-file – Saher Ahwal Dec 15 '14 at 22:01
  • 2
    This does work indeed, but unfortunately you'll still find the full path in the executable. – Julian F. Weinert Mar 04 '15 at 14:44
  • 36
    This could be shortened to `strrchr("/" __FILE__, '/') + 1`. By prepending "/" to `__FILE__`, strrchr is be guaranteed to find something, and thus the conditional ?: is no longer needed. – ɲeuroburɳ Jun 22 '15 at 15:03
  • 1
    Also works great if you use `strstr(__FILE__, "my_source_directory")`. With that you can keep the paths within the project. – Oz. Dec 09 '15 at 21:48
  • `char* fileName = basename(__FILE__);` is dangerous. `basename()` can modify its argument string. Per [POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html): *The `basename()` function may modify the string pointed to by `path`* That could result in a SEGV if the string `__FILE__` expands to is read-only. Also see http://man7.org/linux/man-pages/man3/basename.3.html – Andrew Henle Dec 28 '16 at 20:53
  • You can easily avoid a second call to strrchr with a local variable: `#define __FILENAME__ ({const char * pStr = strrchr(__FILE__, '/'); pStr ? pStr + 1 : __FILE__;})` – mrtumnus Feb 02 '18 at 15:22
  • 4
    For those who interested both *strrchr* and *strstr* work in compile time in this case, tested with gcc 6.3.0 -O2. – Andrew Selivanov Mar 28 '18 at 09:49
  • @AndrewSelivanov how did you verify that this is done at compile time? – Trevor Boyd Smith Jun 06 '18 at 13:01
  • @TrevorBoydSmith built with --save-temps and inspected generated code – Andrew Selivanov Jun 06 '18 at 15:25
  • 1
    A detail: the "__" pre- and postfix is a notation reserved to the implementation. – Pietro Oct 29 '18 at 17:59
  • @mrtumnus - I get the (pedantic) warning: "ISO C++ forbids braced-groups within expressions", pointing to the first parenthesis. – Pietro Oct 29 '18 at 18:17
  • 1
    @Pietro Yeah, the extra parens probably aren't necessary. They carried over from the OP's original statement. – mrtumnus Oct 30 '18 at 19:11
  • 9
    I wonder if the optimizer is able to optimize the calls to `strchr` and `strrchr`. Not to mention references to the `__FILENAME__` macro. If not, this solution is 100% bloat. – l33t Nov 09 '18 at 20:28
  • This does not optimize out the strrchr in the latest MSVC v19. – Gbps Aug 03 '23 at 19:06
60

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

Patrick
  • 2,243
  • 2
  • 23
  • 32
  • 1
    This is a great idea! Much better than pulling in the standard string library to trim the filename, which isn't really an option on most embedded platforms. – thegreendroid Jun 16 '13 at 21:43
  • 16
    I am afraid then this doesn't work for the __FILE__ referenced in header file. – Baiyan Huang Sep 12 '13 at 01:55
  • 5
    Agree with @BaiyanHuang but not sure that the comment is clear. `__FILE__` is not a simple preprocessor symbol, it changes to the current file is often used for emitting the name of the current file (header or source module). This `__FILENAME__` would only have the outermost source – nhed May 01 '14 at 19:59
  • How would you remove the file extension with this? Keeping only basename? – Marc Plano-Lesay Oct 14 '14 at 15:26
  • 1
    @Kernald Try using `$(basename $(abspath $<))` instead of `$(abspath $<)` – Patrick Oct 15 '14 at 13:26
  • Well, that was… unexpectedly simple! Thanks. It works like a charm. – Marc Plano-Lesay Oct 15 '14 at 14:13
  • Great idea! That solved my file path too long in log file problem. – oohtj Jan 13 '15 at 05:28
  • 5
    This answer's solution is not portable since it uses Bourne shell escapes. Better to use CMake to implement it in a clean and portable way. See [define_file_basename_for_sources macro answer](http://stackoverflow.com/a/27990434/994153) here. – Colin D Bennett Jan 16 '15 at 18:29
  • @thegreendroid: there is nothing wrong with using 'strrchr' even on an embedded platform as this function just seeks memory and pretty fast. Furthermore, it's also a part of of both C89 and C99. – firegurafiku Feb 18 '15 at 06:28
  • 4
    The GNU Make variant of this is CFLAGS += -D__FILENAME__=\"$(notdir $<)\" – ctuffli Jul 07 '17 at 20:56
  • 4
    @firegurafiku On embedded platforms, code size is often more of a constraint than speed. If you have debug statements in each file, that can quickly balloon file-size up with full-path strings. I'm dealing with such a platform at the moment, and referencing `__FILE__` everywhere is blowing out of code-space. – mrtumnus Feb 02 '18 at 14:45
  • 2
    Don't work with cmake + ninja: ninja: error: build.ninja:221: bad $-escape (literal $ must be written as $$) – lollo May 03 '18 at 13:08
  • 2
    ⚠️when you follow this method, you break Visual Studio's (as of 2019) /MP parallel compilation. As it batches compilation and having per-file compiling options degrades to batches of size 1. See https://randomascii.wordpress.com/2014/03/22/make-vc-compiles-fast-through-parallel-compilation/ – Gregory Pakosz Apr 24 '19 at 13:47
  • 1
    I got "Using `$<` in a non-suffix rule context is a GNUmake idiom" from a non-GNU make. I used `$(notdir $file)` instead. – twex Aug 13 '20 at 12:39
  • 2
    In addition to working only with the make generator, this will no longer work starting with CMake 3.20. An alternative that is still make-specific is: `set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__FILENAME__='\"$(notdir $(abspath $@))\"'")` – icebp Apr 03 '21 at 07:40
  • I can confirm this answer does not work and broke my cmakelists, I got `c++: fatal error: no input files`. I can also confirm that the above linked helper from [this link](http://stackoverflow.com/a/27990434/994153) works great. Thanks @ColinDBennett – Steven Lu Aug 02 '21 at 05:41
  • Works, but leaves subdirs relative to dir with `CMakeLists.txt`. @Andry's solution leaves _just_ the filename. – automorphic Nov 30 '21 at 00:35
50

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

Old Badman Grey
  • 668
  • 10
  • 19
simon.watts
  • 976
  • 10
  • 14
  • 3
    Option `-ffile-prefix-map` indeed implies both `-fdebug-prefix-map` and `-fmacro-prefix-map` options. See also the references at https://reproducible-builds.org/docs/build-path/ The GCC bug that tracks `-fmacro-prefix-map` and `-ffile-prefix-map` is https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70268 – Lekensteyn Jan 21 '19 at 10:26
  • 2
    If your compiler supports this, like any decently recent version of GCC or Clang does, you should use this instead of the other solutions. It is so much more elegant and lightweight. – ocroquette Jun 01 '22 at 16:48
44

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.

RenatoUtsch
  • 1,449
  • 1
  • 13
  • 20
  • 13
    I didn't understand this at first. I coded up examples and ran them against gcc and clang and they work. I also experiment with just appending various literal numeric values, and that behaves as I expected. Then it finally dawned on me. `__FILE__` is a pointer to an array of bytes. So adding a numeric literal is just pointer addition. Very clever @RenatoUtsch – Daniel Jul 02 '18 at 15:30
  • 1
    It works only if a source file is under cmake list directory. If a source file is outside then it will break, might with access outside of a literal string. So be careful with that. – Andry Jan 23 '19 at 17:17
  • It actually does not reduce the code size. I guess the whole path is still compiled into the binary, just the pointer is modified. – Tarion Feb 20 '19 at 10:29
  • Both the `__FILE__` macro and the SOURCE_PATH_SIZE macros are constants known at compile time. I'd expect modern optimizing compilers to be able to detect that part of the string is not used and simply remove it from the binary. Anyway, I don't think these few bytes would make a significant difference in the binary size, so I wouldn't really care about that. – RenatoUtsch Feb 20 '19 at 17:38
  • 1
    @RenatoUtsch The project I'm working on has a change which just specifies the file name, but has the disadvantage of giving the C file name to the header too. The change was made in order to get reproducible builds. So with gcc with -O2, would the string be indeed optimized and the build made reproducible? – Paul Stelian Dec 04 '19 at 15:34
  • I'd expect that, but you'll have to test it out for yourself. – RenatoUtsch Dec 04 '19 at 19:11
  • This is so simple and clever! – Leonardo Oct 27 '22 at 14:28
27
  • 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__))]);
    }

https://godbolt.org/z/u6s8j3

Andry
  • 2,273
  • 29
  • 28
  • You can define one more helper macro `#define LEAF(FN) (&FN[UTILITY_CONST_EXPR_VALUE(get_file_name_offset(FN))])` and use it like this `printf("%s\n", LEAF(__FILE__));` – psyched Apr 15 '21 at 14:38
  • This should be the answer. It doesn't require cmake, modifying the makefile, or runtime computation. And it can be used by a new __FILENAME__ macro to make it intuitive: `#define __FILENAME__ (__FILE__ + get_file_name_offset(__FILE__))` – automorphic Nov 30 '21 at 00:32
25

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • 2
    Keith Thompson it is great solution, thank You. The only problem is that it seems that this macro cuts `__LINE__` value by one. So `__LINE__` written in line `x` gest evaluated to `x-1`. At least with gcc 5.4. – elklepo Nov 06 '18 at 23:23
  • 2
    @Klepak: You're right, and that's standard behavior. The directive "causes the implementation to behave as if the following sequence of source lines begins with a source line that has a line number as specified by the digit sequence". And it has to be a *digit-sequence*, so you can't use `#line __LINE__-1, "file.c"`. I'll update my answer. – Keith Thompson Nov 07 '18 at 02:11
  • 1
    How would it be for other compilers like Clang, MSVC, or Intel C Compiler? – Franklin Yu Dec 30 '19 at 09:38
  • 3
    At line 0 of a file, I just do: `#line 2 "name.cpp"`. Also as a side note it is nice to know that it does not contaminate other files that `#include` it (at least on MSVC) – Matt Eding Apr 14 '21 at 18:44
20

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

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
  • 4
    Cool answer. You need to use at least `-O1` to use this to be compile-time. – Digital Trauma Mar 14 '17 at 19:59
  • 2
    Isn't this backwards? You want to find the *last* occurrence of '/', which means you should start with the `sizeof(s) > 2` check first. Also, this did not work at compile-time for me, at -Os. The full path strings were present in the output binary. – mrtumnus Feb 02 '18 at 14:42
  • Nope this is not purely compile time solution. – Hadatko Jun 29 '21 at 14:35
10

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

vlp
  • 7,811
  • 2
  • 23
  • 51
strange-corner
  • 404
  • 4
  • 13
  • 1
    seems that gcc has [added it](https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=1a9b3f04c11eb467a8dc504a37dad57a371a0d4c), too – rtrrtr Jul 15 '22 at 12:22
9

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

phuclv
  • 37,963
  • 15
  • 156
  • 475
user7382523
  • 91
  • 1
  • 1
  • in VS2019, `/FC` is forced if using the [Edit & Continue](https://learn.microsoft.com/en-us/cpp/build/reference/z7-zi-zi-debug-information-format?view=vs-2019) `/ZI` d/b. – Laurie Stearn Sep 11 '20 at 09:11
  • Without the /FC option, `__FILE__` uses the relative path instead of the absolute path. Although it is shorter, it is not a "file name without path" solution. – Cem Polat Apr 06 '22 at 09:47
  • At least in VS2019 & VS2022 the default for the underlying setting in the project files (`UseFullPaths`) defaults to `true` unless overridden at a more local level (such as in the project file). See `Microsoft.Cl.Common.props` ... – 0xC0000022L Jan 19 '23 at 16:14
8

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.

Levi.G
  • 93
  • 1
  • 6
  • 1
    This does not work for contents coming from include files. – chqrlie Mar 30 '20 at 11:52
  • can you post some code? A common case is to set variables in local scope and use it in another. – Levi.G Apr 10 '20 at 10:28
  • 1
    For example if you use `__FILE__` with your definition to produce a diagnostic in an inline function defined in a header file, the runtime diagnostic will report the name of the file passed to the compiler instead of the name of the include file, whereas the line number would refer to the include file. – chqrlie Apr 10 '20 at 10:34
  • 1
    yes, it designed to be that, for the most common usage is `#define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args)`. when using the `LOG()` macro, you do not really want to see `log.h` in messages. after all, the `__FILE__` macro is expanded in every C/Cpp file (compile unit) instead of the included files. – Levi.G Jun 14 '20 at 08:24
8

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

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

magic

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.

0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
Paul
  • 6,061
  • 6
  • 39
  • 70
6

This behaviour has been implemented in some compilers, you can get basename of the compiled file by using:

Charlie Leger
  • 61
  • 1
  • 5
4

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 $<))\"'")
fragmentedreality
  • 1,287
  • 9
  • 31
4

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__);
Dmitry Gordon
  • 2,229
  • 12
  • 20
4

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.

Prof. Falken
  • 24,226
  • 19
  • 100
  • 173
  • 2
    @mahmood: `char file_copy[] = __FILE__; const char *filename = basename(__FILE__);`. The reason for the copy is that basename can modify the input string. You also have to watch out that the result pointer is only good until `basename` is called again. This means it isn't thread-safe. – Steve Jessop Dec 13 '11 at 11:26
  • @SteveJessop, ah I forgot. True. – Prof. Falken Dec 13 '11 at 11:53
  • 1
    @Amigable: to be fair, I suspect that `basename` in fact will not modify the input string that results from `__FILE__`, because the input string doesn't have a `/` at the end and so there's no need for modification. So you might get away with it, but I figure the first time someone sees `basename`, they should see it with all the restrictions. – Steve Jessop Dec 13 '11 at 11:57
  • @SteveJessop the BSd man page for basename() mention that legacy version of basename() takes a const char* and does not modify the string. The linux man page mentions nothing about const but mentions that it can return a part of the argument string. So, best be conservative dealing with basename(). – Prof. Falken Dec 13 '11 at 12:20
  • @SteveJessop, hehe, I only *now* after looking at your comment carefully, after four years, **realize** that `/` at the end of the string means basename may have a good reason to modify its argument. – Prof. Falken Jun 08 '15 at 17:49
3

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.

3

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;
}
Kunis
  • 138
  • 1
  • 7
3

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

#define __FILENAME__ std::max<const char*>(__FILE__,\
    std::max(strrchr(__FILE__, '\\')+1, strrchr(__FILE__, '/')+1))
Luc Bloom
  • 1,120
  • 12
  • 18
  • Why do you need `std::max` instead of just `std::max`? – chqrlie Mar 30 '20 at 11:51
  • Good solution except `std::max` requires `` which I would rather not include in a header file. Another option can be checking platform by `#ifdef _WIN32` since `__FILE__` will have consistent separator characters. – Burak Oct 15 '21 at 12:16
  • 1
    @Burak then any other simple "max" function will suffice. #define PATH_SEPARATOR will also help of course. – Luc Bloom May 09 '22 at 10:15
3

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.

Sean
  • 60,939
  • 11
  • 97
  • 136
  • 1
    the `strrchr` answer could plausibly be computed at compile-time, although of course still not by the preprocessor. I don't know whether gcc actually does it, I haven't checked, but I'm pretty sure that it does compute `strlen` of string literals at compile-time. – Steve Jessop Dec 13 '11 at 11:32
  • @Steve - maybe, but that's a big dependency on compiler specific behaviour. – Sean Dec 13 '11 at 11:36
  • I don't think it is a big dependency, because I very much doubt that this code is performance-critical. And if it is, move it out of the loop. In cases where this is a huge deal, because you absolutely need a string literal containing just the basename, you could perhaps compute the right string at *build* time by running some script over the source. – Steve Jessop Dec 13 '11 at 11:37
  • 5
    It may not be performance critical, but it can easily be seen as privacy critical. There's no real good reason for revealing my per-customer organizational practices in strings frozen into a released EXE file. Worse, for applications created on behalf of a customer, those strings might reveal things my customer might prefer not to, such as not being the author of their own product. Since `__FILE__` is invoked implicitly by `assert()`, this leak can occur without any other overt act. – RBerteig Sep 13 '12 at 20:58
  • @RBerteig the basename of `__FILE__` itself may also reveal things the customer might prefer not to, so using `__FILE__` anywhere at all -- whether it contains the full absolute pathname or just the basename -- has the same issues that you pointed out. In this situation all output will need to be scrutinized and a special API should be introduced for output to customers. The rest of the output should be dumped to /dev/NULL or stdout and stderr should be closed. :-) – tchen Jan 11 '13 at 05:23
  • There *is* a way to do it purely on compile time, you just need to be a little bit smart with the build configuration. See my answer for more details. – RenatoUtsch Sep 20 '17 at 20:08
3

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.

hmjd
  • 120,187
  • 20
  • 207
  • 252
2

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.

  • 3
    `push_macro` and `pop_macro` are non-standard. (gcc supports them for compatibility with Microsoft Windows compilers.) In any case, there's no point in pushing and popping the definition of `__FILE__`; the restored value won't be used after the end of the source file anyway. A cleaner way to change the value of `__FILE__` is `#line __LINE__ "foobar.c"` – Keith Thompson Feb 03 '16 at 16:30
  • 1
    And this causes an internal error in gcc's preprocessor. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69665 – Keith Thompson Feb 04 '16 at 02:06
2

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: 
2

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

IvanR
  • 533
  • 4
  • 23
  • 1
    CMake 3.4+ documentation states that "This variable has no effect. The partially implemented effect it had in previous releases was removed in CMake 3.4." If it worked for you with 3.13, there's another reason for that. – mike.dld Jun 19 '19 at 06:53
2

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.

Cem Polat
  • 101
  • 1
  • 7
  • The fact that the question uses "/" as path separator points to a non-Windows compiler. I doubt @mahmood was using MSVC; some comments state he was using gcc but I could not locate where he said so. – Ramón Gil Moreno Apr 06 '22 at 17:39
1

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.

beavis9k
  • 11
  • 2
0
#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
Dimfred
  • 182
  • 2
  • 16
0

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.

  • Why should you search for backslashes first? If `__FILE__` contains mixed separators, e.g. `c:\abc\xyz/file.h`, then the macro does not work. – insaneinvader Mar 05 '23 at 22:01
0

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__, '/')
Albert K
  • 1
  • 2
  • 3
    Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Mark Rotteveel Mar 05 '21 at 14:53
0

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
serghei
  • 3,069
  • 2
  • 30
  • 48
0

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.

  • This is a C++ solution. The OP asked in a 'C' environment. This doesn't work for a 'C' program. – Brad Sep 12 '22 at 18:02
0

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.

ziu
  • 2,634
  • 2
  • 24
  • 39
  • 6
    makes no difference. I used `__FILE__` and `__BASE_FILE__` however they both show full path to file – mahmood Dec 13 '11 at 11:52
  • how do you invoke the compiler ? – ziu Dec 13 '11 at 11:57
  • 2
    Then I bet SCONS is calling gcc like this `gcc /absolute/path/to/file.c`. If you find a way to change this behavior (opening another question on SO, lol?), you do not need to modify the string at runtime – ziu Dec 13 '11 at 12:05
  • 19
    This answer is 100% wrong. `__BASE_FILE__` (as the docs say, albeit unclearly) produces the name of *the file specified on the command line*, e.g. `test.c` or `/tmp/test.c` depending on how you invoked the compiler. That's exactly the same thing as `__FILE__`, except if you're inside a header file, in which case `__FILE__` produces the name of the current file (e.g. `foo.h`) whereas `__BASE_FILE__` continues to produce `test.c`. – Quuxplusone Oct 01 '15 at 22:46