You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++如何在编译期提取__FILE__文件名并拼接__LINE__行号

编译期拼接文件名与行号实现方案

问题背景

尝试在编译期将带路径的源文件信息转换为文件名:行号的格式(例如将/tmp/main.cpp对应位置转换为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;
}

编译时抛出如下错误:

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]’

错误原因

C++17标准中std::array仅const版本的下标运算符是constexpr的,非const重载不支持常量求值。原代码直接对formatFilename返回的临时数组对象调用[0],会匹配非const版本的下标运算符,因此触发编译错误。此外临时对象的地址不具备常量属性,无法直接用于初始化constexpr指针。

修正实现(C++17兼容)

修改点:

  • 用static constexpr变量存储拼接结果,保证对象具备静态存储期、地址为常量
  • 调用const版本的数组访问接口,匹配constexpr重载
  • 修正循环索引的类型不匹配问题
#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;
  return i - str;
}

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

int main() {
  static constexpr auto file_loc = formatFilename<str_size(file_name(__FILE__)), str_size(LINE_STRING)>(
      file_name(__FILE__), LINE_STRING);
  constexpr const char* p = file_loc.data();
  cout << p << endl;
  return 0;
}

更简便的实现方式

  • 如果使用C++20及以上标准,std::array的非const下标运算符、data()方法均已支持constexpr,甚至可以直接用constexpr std::string完成字符串拼接,无需手动操作字符数组。
  • GCC、Clang编译器提供内置宏__FILE_NAME__,直接展开为不带路径的当前源文件名,不需要手动实现路径裁剪逻辑,可以直接和行号拼接使用。

内容的提问来源于stack exchange,提问作者user8101155

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 09:12:17