Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extract the source filename without path and suffix at compile time?

Tags:

c++

c

c++11

c++14

c11

Using both gcc with -std=c11 and g++ with -std=c++14.

E.g. for a file named src/dir/Hello.cxx it should expand to something like e.g.:

const char basename[] = "Hello"; 

or

const char basename[] = getStaticBasename(__FILE__); 

as where getStaticBasename() is a macro (for C sources) or constexpr function (for C++ sources) which results to "Hello".

I have to avoid splitting the string from __FILE__ at runtime, because the path and suffix must not be compiled into the executable in any way.

The solution must be without dependencies to huge libraries such as boost.

As I have no makefiles, solutions like this cannot be used in my case.

Did one have a solution for that?

Edit 2015-07-02:

  • I have no influence on how the compiler and linker was invoked (sometimes via makefile, sometimes from command line, or some IDE (Eclipse CDT managed make, Crossworks, Xcode et cetera. So the solution needs to be in code only.
  • My use case is to provide some kind of "generic region identifier" for a small footprint logging solution. The application code (which uses my logger) should only #include <Joe/Logger.h> and within the later calls to e.g. LOG_DEBUG(...) I'll implicitely take use of the automatically generated "generic region identifier".
  • My current solution is that the application code have to declare a JOE_LOG_FILE_REGION(Hello); (after #include <Joe/Logger.h>) before it could place LOG_DEBUG(...) in its code.
like image 325
Joe Avatar asked Jun 25 '15 12:06

Joe


People also ask

Which function is used to retrieve the filename from the given path?

GetFileName(String) Returns the file name and extension of the specified path string.

What is __ FILE __ in C++?

The __FILE__ macro expands to a string whose contents are the filename, surrounded by double quotation marks ( " " ). If you change the line number and filename, the compiler ignores the previous values and continues processing with the new values. The #line directive is typically used by program generators.


2 Answers

1. gcc builtin function can get the file name of a full path at compile time.

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

or

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

2. c++11 constexpr also can do this at compile time.

c++11 constexpr function can only use a return-statement.

example:

#include <stdio.h>  constexpr const char* str_end(const char *str) {     return *str ? str_end(str + 1) : str; }  constexpr bool str_slant(const char *str) {     return *str == '/' ? true : (*str ? str_slant(str + 1) : false); }  constexpr const char* r_slant(const char* str) {     return *str == '/' ? (str + 1) : r_slant(str - 1); } constexpr const char* file_name(const char* str) {     return str_slant(str) ? r_slant(str_end(str)) : str; }  int main() {     constexpr const char *const_file = file_name(__FILE__);     puts(const_file);     return 0; } 

source file name is foo/foo1/foo2/foo3/foo4.cpp

use g++ -o foo.exe foo/foo1/foo2/foo3/foo4.cpp -std=c++11 --save-temps to compile this file.

you can see this.

.file   "foo4.cpp"         .section        .rodata .LC0:         .string "foo/foo1/foo2/foo3/foo4.cpp"         .text         .globl  main         .type   main, @function main: .LFB4:         .cfi_startproc         pushq   %rbp         .cfi_def_cfa_offset 16         .cfi_offset 6, -16         movq    %rsp, %rbp         .cfi_def_cfa_register 6         subq    $16, %rsp         movq    $.LC0+19, -8(%rbp)          movl    $.LC0+19, %edi         call    puts         movl    $0, %eax         leave         .cfi_def_cfa 7, 8         ret         .cfi_endproc .LFE4:         .size   main, .-main         .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"         .section        .note.GNU-stack,"",@progbits 

movl $.LC0+19, %edi .LC0 + 19 is the address of file name string without path and suffix

3. c++14 constexpr function can do this in a simple way

#include <iostream>  constexpr const char* file_name(const char* path) {     const char* file = path;     while (*path) {         if (*path++ == '/') {             file = path;         }     }     return file; }  int main() {     constexpr const char* file = file_name(__FILE__);     std::cout << file << std::endl;     return 0; } 

c++14 constexpr function can use loop and local variable.

the file_name function will replace with a address of const char * at compiler time. ~

like image 141
pexeer Avatar answered Sep 28 '22 07:09

pexeer


extract the base filename at compile time with no preprocessor tricks and no external scripts? c++14? no problem sir.

#include <iostream> #include <string>  using namespace std;  namespace detail {     constexpr bool is_path_sep(char c) {         return c == '/' || c == '\\';     }      constexpr const char* strip_path(const char* path)     {         auto lastname = path;         for (auto p = path ; *p ; ++p) {             if (is_path_sep(*p) && *(p+1)) lastname = p+1;         }         return lastname;     }      struct basename_impl     {         constexpr basename_impl(const char* begin, const char* end)         : _begin(begin), _end(end)         {}          void write(std::ostream& os) const {             os.write(_begin, _end - _begin);         }          std::string as_string() const {             return std::string(_begin, _end);         }          const char* const _begin;         const char* const _end;     };      inline std::ostream& operator<<(std::ostream& os, const basename_impl& bi) {         bi.write(os);         return os;     }      inline std::string to_string(const basename_impl& bi) {         return bi.as_string();     }      constexpr const char* last_dot_of(const char* p) {         const char* last_dot = nullptr;         for ( ; *p ; ++p) {             if (*p == '.')                 last_dot = p;         }         return last_dot ? last_dot : p;     } }  // the filename with extension but no path constexpr auto filename = detail::strip_path(__FILE__); constexpr auto basename = detail::basename_impl(filename, detail::last_dot_of(filename));  auto main() -> int {     cout << filename << endl;     cout << basename << endl;      cout << to_string(basename) << endl;      return 0; } 
like image 40
Richard Hodges Avatar answered Sep 28 '22 07:09

Richard Hodges