求助:完成C++函数名解析标准化的constexpr静态存储实现
解决方案:编译期生成静态存储的精简函数名
要实现constexpr编译期解析+模板静态存储的方案,核心是利用模板实例的唯一性,将每个函数的精简名编译期计算后存入对应模板实例的静态成员中,避免冗余的__PRETTY_FUNCTION__字符串进入二进制。
核心思路
- constexpr解析函数:将你已有的函数名清理逻辑改造成完全constexpr兼容的实现,确保编译期就能完成字符串处理。
- 模板静态存储:通过函数指针作为模板参数,让每个函数对应唯一的模板实例,其静态成员存储编译期计算好的精简函数名,保证全局仅存一份。
代码实现
1. constexpr精简函数名解析
先把你的解析逻辑适配成constexpr版本(以下是示例逻辑,可替换为你已有的成熟实现):
#include <string_view> constexpr std::string_view clean_function_name(std::string_view raw_name) { // 跳过开头空白 size_t start = raw_name.find_first_not_of(" \t"); if (start == std::string_view::npos) return ""; raw_name = raw_name.substr(start); // 截断参数列表部分 size_t paren_pos = raw_name.find('('); if (paren_pos != std::string_view::npos) { raw_name = raw_name.substr(0, paren_pos); } // 移除返回值前缀(适配多数编译器格式) size_t last_space = raw_name.find_last_of(' '); if (last_space != std::string_view::npos) { raw_name = raw_name.substr(last_space + 1); } // 特殊处理lambda if (raw_name.find("lambda") != std::string_view::npos) { return "lambda"; } // 适配MSVC的__FUNCSIG__格式(可选) size_t msvc_scope = raw_name.rfind("::"); if (msvc_scope != std::string_view::npos && raw_name.substr(0, msvc_scope).find("__cdecl") != std::string_view::npos) { raw_name = raw_name.substr(msvc_scope + 2); } return raw_name; }
2. 模板静态存储结构体
用函数指针作为模板参数,确保每个函数对应唯一的模板实例,静态成员存储编译期计算的精简名:
template<typename FuncType, FuncType FuncPtr> struct StaticFunctionName { // 编译期调用解析函数,结果存入静态成员 static constexpr std::string_view value = clean_function_name(__PRETTY_FUNCTION__); };
3. 辅助宏简化调用
针对普通函数和成员函数分别定义宏,减少重复代码:
// 普通函数调用宏 #define GET_CLEAN_FUNC_NAME() \ StaticFunctionName<decltype(&__func__), &__func__>::value // 成员函数调用宏(需在成员函数内部使用) #define GET_CLEAN_MEMBER_FUNC_NAME() \ StaticFunctionName<decltype(&std::remove_reference_t<decltype(*this)>::__func__), \ &std::remove_reference_t<decltype(*this)>::__func__>::value
4. 测试示例
#include <iostream> void plain_function() { constexpr auto name = GET_CLEAN_FUNC_NAME(); std::cout << name << '\n'; // 输出: plain_function } class TestClass { public: void member_function() { constexpr auto name = GET_CLEAN_MEMBER_FUNC_NAME(); std::cout << name << '\n'; // 输出: member_function } }; template<typename T> void template_function(T) { constexpr auto name = GET_CLEAN_FUNC_NAME(); std::cout << name << '\n'; // 输出: template_function } int main() { plain_function(); TestClass{}.member_function(); template_function(42); template_function("test"); // Lambda测试 auto lambda = []() { constexpr auto name = GET_CLEAN_FUNC_NAME(); std::cout << name << '\n'; // 输出: lambda }; lambda(); return 0; }
为什么能避免代码膨胀?
- 编译期计算:
clean_function_name在编译期完成所有字符串处理,__PRETTY_FUNCTION__的原始冗余字符串仅在编译阶段被读取,不会进入最终二进制。 - 模板实例唯一性:每个函数(包括模板函数的不同实例、成员函数)对应唯一的
StaticFunctionName模板实例,其静态成员value全局仅存储一份,不会重复生成冗余数据。
关键注意事项
- 编译器兼容性:不同编译器的
__PRETTY_FUNCTION__/__FUNCSIG__格式不同,需在clean_function_name中适配(你的现有工具应该已经处理了这部分,只需迁移到constexpr逻辑)。 - C++版本要求:需使用C17及以上(支持
std::string_view的constexpr成员函数),C20可获得更完善的字符串操作支持。 - 特殊函数处理:lambda、虚函数、重载函数的
__func__/函数指针推导需单独适配,比如lambda的__func__是operator(),需在解析逻辑中识别替换。
内容的提问来源于stack exchange,提问作者Nem Yr
相关产品推荐
相关产品推荐

