Thursday, March 3, 2011

Learning the source code's filename at compile time

I'm using GCC; __FILE__ returns the current source file's entire path and name. Is there a way to get just the file's name and not its whole path too (at compile time)? Is it possible to do this in a portable way? Can template meta programming be applied to strings?

I am using this in an error logging macro. I really do not want my source's full path making its way into the executable.

Thanks!

From stackoverflow
  • You might be able to do it with template metaprogramming, but there's no built-in way to do it.

    EDIT: Hm, correction. According to one page I just saw, GCC uses the path that it's given for the file. If it's given the full name, it'll embed it; if it's only given a relative one, it'll only embed that. I haven't tried it myself though.

    peterchen : I thought about the metaprogramming, but I never even suceeded in passing __FILE__ as template argument.
    Head Geek : I'm looking into how to do it via metaprogramming, mostly for my own personal enlightenment. It looks like it should be possible. I'll edit my answer if/when I come up with a solution.
  • I don't know of a direct way. You could use:

    #line 1 "filename.c"
    

    at the top of the source file to set the value of __FILE__, but I'm not sure that that's much better than hard coding it. or just using a #define to create your own macro.

    Another option might be to pass the name from your Makefile using -D and $(shell basename $<)

    Edit: If you use a #define or the -D option, you should create your own new name and not try to redefine __FILE__.

    Martin York : Messing with pre-defined macros is not a good idea. As they are not defined in the same way as other macros attempts to change them will be compiler specific.
  • You could use:

    bool IsDebugBuild()
    {
        return !NDEBUG;
    }
    

    Or, you could use NDEBUG in your Macro to turn on/off those file paths.

  • What does your error logging macro do? I would presume at some point the macro eventually calls a function of some kind in order to do the logging, why not have the called function strip off the path component at runtime?

    #define LOG(message) _log(__FILE__, message)
    
    void _log(file, message)
    {
      #ifndef DEBUG
      strippath(file); // in some suitable way
      #endif
    
      cerr << "Log: " << file << ": " << message; // or whatever
    }
    
    paxdiablo : That stops the full filename from being output to the logs but still has it contained within the executable. I think that's what the questioner was after. Still, it's a good solution so no downvote.
    Martin York : Read this article about underscores: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier
    Imbue : I'd much rather have a compile time solution. I think it's sloppy to have development paths in the finial executable.
  • If you're using a make program, you should be able to munge the filename beforehand and pass it as a macro to gcc to be used in your program.

    In your makefile, change the line:

    file.o: file.c
        gcc -c -o file.o src/file.c
    

    to:

    file.o: src/file.c
        gcc "-D__MYFILE__=\"`basename $<`\"" -c -o file.o src/file.c
    

    This will allow you to use __MYFILE__ in your code instead of __FILE__.

    The use of basename of the source file ($<) means you can use it in generalized rules such as ".c.o".

    The following code illustrates how it works.

    File makefile:

    mainprog: main.o makefile
        gcc -o mainprog main.o
    
    main.o: src/main.c makefile
        gcc "-D__MYFILE__=\"`basename $<`\"" -c -o main.o src/main.c
    

    File src/main.c:

    #include <stdio.h>
    
    int main (int argc, char *argv[]) {
        printf ("file = %s\n", __MYFILE__);
        return 0;
    }
    

    Run from the shell:

    pax@pax-desktop:~$ mainprog
    file = main.c
    pax@pax-desktop:~$
    

    Note the "file =" line which contains only the basename of the file, not the dirname.

  • You can take __FILE__ and the strip off the part of path you don't want (programatically). If basedir satisfies your needs, then fine. Otherwise, get source dir root from your build system, and the rest should be doable.

  • Consider this simple source code:

    #include <stdio.h>
    int main(void)
    {
        puts(__FILE__);
        return(0);
    }
    

    On Solaris, with GCC 4.3.1, if I compile this using:

    gcc -o x x.c && ./x
    

    the output is 'x.c' If I compile it using:

    gcc -o x $PWD/x.c && ./x
    

    then __FILE__ maps to the full path ('/work1/jleffler/tmp/x.c'). If I compile it using:

    gcc -o x ../tmp/x.c && ./x
    

    then __FILE__ maps to '../tmp/x.c'.

    So, basically, __FILE__ is the pathname of the source file. If you build with the name you want to see in the object, all is well.

    If that is impossible (for whatever reason), then you will have to get into the fixes suggested by other people.

  • Taking the idea from Glomek, it can be automated like this:

    Source file x.c

    #line 1 MY_FILE_NAME
    #include <stdio.h>
    
    int main(void)
    {
        puts(__FILE__);
        return(0);
    }
    

    Compilation line (beware the single quotes outside the double quotes):

    gcc -DMY_FILE_NAME='"abcd.c"' -o x x.c
    

    The output is 'abcd.c'.

  • Could be done ONLY programmatically.

    maybe this is useful...

    filename = FILE len = strlen(filename)

    char temp = &filename[len -1] while(temp!= filename) if(*temp == '\') break;

  • You can assign __FILE__ to a string, and then call _splitpath() to rip the pieces out of it. This might be a Windows/MSVC-only solution, honestly I don't know.

    I know you were looking for a compile-time solution and this is a run-time solution, but I figured since you were using the filename to do (presumably run-time) error logging, this could be a simple straightforward way to get you what you need.

    j_random_hacker : This is a good idea, but it's been suggested already, twice. Please upvote an existing solution when you can.
    John Dibling : Nobody else suggested using _splitpath(). I have found that many people don't know such a function exists, and they end up parsing the string themselves. My solution will remain.
    j_random_hacker : Fair enough, +1.
  • Just got the same issue; found a different resolution, just thought I'd share it:

    In a header file included in all my other files:

    static char * file_bname = NULL;
    #define __STRIPPED_FILE__   (file_bname ?: (file_bname = basename(__FILE__)))
    

    Hope this is useful to someone else as well :)

0 comments:

Post a Comment