Home > front end >  How to get filename from __FILE__ and concat with __LINE__ at compile time
How to get filename from __FILE__ and concat with __LINE__ at compile time

Time:06-18

I'm trying to concat filename and line without path at compile time. Like /tmp/main.cpp20 -> main.cpp:20

#include <array>
#include <iostream>
using namespace std;

#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#define LINE_STRING STRINGIZE(__LINE__)

constexpr const char* file_name(const char* path) {
  const char* file = path;
  while (*path) {
    if (*path   == '/') {
      file = path;
    }
  }
  return file;
}

constexpr std::size_t str_size(const char* str) {
  auto i = str;
  while (*i != '\0')   i;
  const auto length = i - str;
  return length;
}

template <std::size_t N1, std::size_t N2>
constexpr std::array<char, N1   N2   2> formatFilename(const char* name,
                                                       const char* line) {
  auto total_size = N1   N2   2;
  std::array<char, N1   N2   2> res{};
  std::size_t i = 0;
  for (; i < N1;   i) {
    res[i] = name[i];
  }
  res[i  ] = ':';
  for (int j = 0; j < N2;   j) {
    res[i   j] = line[j];
  }
  res[total_size - 1] = '\0';
  return res;
}

int main() {
  constexpr char *p = &(
      formatFilename<str_size(file_name(__FILE__)), str_size(LINE_STRING)>(
          file_name(__FILE__), LINE_STRING)[0]);
  cout << p << endl;
}

But it seems not work and return a compilation error

main.cpp: In function ‘int main()’: main.cpp:46:46: error: call to non-‘constexpr’ function ‘std::array<_Tp, _Nm>::value_type& std::array<_Tp, _Nm>::operator[](std::array<_Tp, _Nm>::size_type) [with _Tp = char; long unsigned int _Nm = 12; std::array<_Tp, _Nm>::reference = char&; std::array<_Tp, _Nm>::value_type = char; std::array<_Tp, _Nm>::size_type = long unsigned int]’

Is there any way to do it at compile time?

CodePudding user response:

You are taking an address of a temporary array. Actually first store the array, then take the address.

  constexpr auto p = // stores the array
      formatFilename<str_size(file_name(__FILE__)), str_size(LINE_STRING)>(
          file_name(__FILE__), LINE_STRING);
  // than take the address
  cout << p.data() << endl;

CodePudding user response:

For your specific case (getting the filename without path and the line number) there are a few easier approaches that you might want to use:


1. Using __FILE_NAME__ instead of __FILE__

Both gcc and clang support the __FILE_NAME__ macro, that just resolves to the filename instead of the full file path.

Utilizing those you can write it as a single macro:


#define STRINGIFY(x) STRINGIFY_IMPL(x)
#define STRINGIFY_IMPL(x) #x

#define FILENAME __FILE_NAME__ ":" STRINGIFY(__LINE__)

// Usage:
constexpr char* foo = FILENAME;
std::cout << foo << std::endl;

msvc unfortunately doesn't offer the __FILE_NAME__ macro, but you could utilize the /FC compiler switch to specify if you want the full path for __FILE__ or just the filename.

--

2. Let the compiler do the string concatenation

Adjacent character sequences are automatically combined by the compiler - e.g. "a" "b" "c" would be combined into "abc".

You can use this to let the compiler do the concatenation for you.
After that we can just increment the pointer to that combined string past the last slash to trim off the path:

#define STRINGIFY(x) STRINGIFY_IMPL(x)
#define STRINGIFY_IMPL(x) #x
#define FILENAME __FILE__ ":" STRINGIFY(__LINE__)

constexpr const char* filename_without_path(const char* path) {
    const char* result = path;
    while(*path != '\0')
        if(*path   == '/') result = path;

    return result;
}

// Usage:
constexpr const char* foo = filename_without_path(FILENAME);
std::cout << foo << std::endl;

  • Related