The most voted solution does not rely answer the OP, because the complete file path is stored in the binary and only a pointer to the last part of the path is computed (from the last '/' character) and used.
See assembly output of the proposed solution in @pexeer answer:
.LC0:
.string "/app/example.cpp"
main:
push rax
mov esi, OFFSET FLAT:.LC0+5
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
xor eax, eax
pop rdx
ret
_GLOBAL__sub_I_main:
push rax
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
pop rcx
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
jmp __cxa_atexit
To avoid storing the complete file path, you'll need something like this:
#include <iostream>
#include <utility>
constexpr const char* file_name(const char* path) {
const char* file = path;
while (*path) {
if (*path++ == '/') {
file = path;
}
}
return file;
}
constexpr size_t file_length(const char * path) {
size_t i = 0;
const char * file = file_name(path);
while (*file) { i ++; file++; }
return i;
}
template<std::size_t... I>
const char * print_impl(std::index_sequence<I...>) {
static const char file[file_length(__FILE__)+1] = { file_name(__FILE__)[I]...};
return file;
}
inline const char* print_file() {
return print_impl(std::make_index_sequence<file_length(__FILE__) + 1>());
}
int main() {
std::cout<<print_file()<<std::endl;
return 0;
}
and you'll get this assembly output (where the complete file path isn't stored):
main:
push rax
mov esi, OFFSET FLAT:print_impl<0ul, 1ul, 2ul, 3ul, 4ul, 5ul, 6ul, 7ul, 8ul, 9ul, 10ul, 11ul>(std::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul, 5ul, 6ul, 7ul, 8ul, 9ul, 10ul, 11ul>)::file
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
xor eax, eax
pop rdx
ret
_GLOBAL__sub_I_main:
push rax
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
pop rcx
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
jmp __cxa_atexit
print_impl<0ul, 1ul, 2ul, 3ul, 4ul, 5ul, 6ul, 7ul, 8ul, 9ul, 10ul, 11ul>(std::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul, 5ul, 6ul, 7ul, 8ul, 9ul, 10ul, 11ul>)::file:
.string "example.cpp"
Example here
The basic idea here is to construct a statically initialized char array containing only the string you want (and not a pointer to a static char array containing the full file path). Deducing the file length is trivial but required since we can't call strlen
in a constexpr function.
Then the trick is to use a integer sequence as indices in the file's pointed array (like natural declaration: const char f[] = {"str"[0], "str"[1], ...}
). The integer sequence can be used in variadic template instantiation, so it must be called in such context.
GCC leaks the print_impl
function as a symbol (so it's likely larger that the file's full path), but it can be stripped later on at linker step (or with strip --strip-all /path/to/binary
)
constexpr
s. – Roister__FILE__
macro shows full path – Rebuttermakefile
solution can be adapted to the command line. Just have some script to build your thing, or even use some script instead ofg++
– Cheops