如何将CMake的CMAKE_SOURCE_DIR传入C++源码以缩短日志路径?
解决CMake传递CMAKE_SOURCE_DIR到C++代码并缩短std::source_location路径的问题
1. 修复编译定义的错误
你遇到的macro names must be identifiers错误,是因为手动添加-D和引号的写法不符合CMake规范。CMake的add_compile_definitions不需要手动指定-D参数,直接传入宏名和值即可,CMake会自动处理字符串的引号转义。
正确写法(全局生效):
add_compile_definitions(SOURCE_ROOT="${CMAKE_SOURCE_DIR}")
更推荐用target_compile_definitions(仅作用于指定目标,模块化更好):
target_compile_definitions(你的目标名称 PRIVATE SOURCE_ROOT="${CMAKE_SOURCE_DIR}")
2. 更可靠的替代方案:用configure_file生成配置头文件
如果项目路径包含特殊字符(如空格),或者想更清晰管理配置宏,建议使用configure_file:
- 创建模板头文件
config.h.in:
#pragma once #define SOURCE_ROOT "@CMAKE_SOURCE_DIR@"
- 在CMakeLists.txt中添加配置逻辑:
configure_file( ${CMAKE_SOURCE_DIR}/config.h.in ${CMAKE_BINARY_DIR}/generated/config.h @ONLY ) # 将生成头文件的目录添加到目标的包含路径 target_include_directories(你的目标名称 PRIVATE ${CMAKE_BINARY_DIR}/generated)
之后在C++代码中直接包含#include <config.h>即可获取SOURCE_ROOT宏。
3. 在C++中实现路径缩短逻辑
拿到SOURCE_ROOT后,编写工具函数处理std::source_location返回的绝对路径:
#include <source_location> #include <string_view> #include <string> // 缩短路径:去掉CMAKE_SOURCE_DIR前缀 std::string shorten_source_path(std::string_view full_path) { constexpr std::string_view root = SOURCE_ROOT; // C++20及以上支持starts_with if (full_path.starts_with(root)) { size_t skip_len = root.size(); // 跳过根路径后的路径分隔符(如果存在) if (skip_len < full_path.size() && (full_path[skip_len] == '/' || full_path[skip_len] == '\\')) { ++skip_len; } return std::string(full_path.substr(skip_len)); } return std::string(full_path); } // 日志示例函数 void log(const std::string& msg, const std::source_location& loc = std::source_location::current()) { auto short_path = shorten_source_path(loc.file_name()); printf("[%s:%d] %s\n", short_path.c_str(), loc.line(), msg.c_str()); }
如果使用C++17及更早标准,替换starts_with为find实现:
size_t pos = full_path.find(root); if (pos == 0) { size_t skip_len = root.size(); if (skip_len < full_path.size() && (full_path[skip_len] == '/' || full_path[skip_len] == '\\')) { ++skip_len; } return std::string(full_path.substr(skip_len)); }
4. 跨平台兼容处理
- 路径分隔符:Windows下路径用
\,可以在CMake中先统一转换为/:
file(TO_CMAKE_PATH "${CMAKE_SOURCE_DIR}" CMAKE_SOURCE_DIR_CLEAN) target_compile_definitions(你的目标名称 PRIVATE SOURCE_ROOT="${CMAKE_SOURCE_DIR_CLEAN}")
file(TO_CMAKE_PATH)会将路径转换为CMake统一的/分隔符,避免C++代码中处理两种分隔符。
- 编译器兼容性:上述代码在GCC、Clang、MSVC下均能正常工作,需确保项目启用C20及以上标准(
std::source_location依赖C20)。
内容的提问来源于stack exchange,提问作者Benjamin Buch
相关产品推荐
相关产品推荐

