【发布时间】:2025-11-26 23:15:03
【问题描述】:
我刚刚注意到 __func__、__FUNCTION__ 和 __PRETTY_FUNCTION__ 不被视为预处理器宏,并且在标准的 16.8 预定义宏名称部分中没有提及(N4527 Working Draft)。
这意味着它们不能用于phase 6的字符串连接技巧:
// Valid
constexpr char timestamp[]{__FILE__ " has been compiled: " __DATE__ " " __TIME__};
// Not valid!!!
template <typename T>
void die() { throw std::runtime_error{"Error detected in " __PRETTY_FUNCTION__}; }
据我所知,__FILE__、__DATE__ 和 __TIME__ 被转换为标准规定的字符串文字:
16.8 预定义的宏名称 [cpp.predefined]
__DATE__源文件的翻译日期:
"Mmm dd yyyy"形式的字符串文字,其中月份的名称与asctime函数生成的月份名称相同,第一个如果值小于 10,则 dd 的字符为空格字符。如果翻译日期不是 可用,应提供实现定义的有效日期。
__FILE__当前源文件的假定名称(字符串文字)。
__TIME__源文件的翻译时间:
"hh:mm:ss"形式的字符串文字,与 asctime 函数生成的时间相同。
__func__ 被标准提及为以下形式的函数局部预定义变量:
static const char __func__[] = "function-name ";
所以事实是它是一个局部变量,因此字符串连接技巧不适用于它。
至于__FUNCTION__ 和__PRETTY_FUNCTION__ 并没有在标准中提及(是否定义了实现?),但认为它们的行为会像__func__ 是一个相当安全的选择。
所以问题是:为什么__func__、__FUNCTION__ 和__PRETTY_FUNCTION__ 是函数局部静态常量数组,而__FILE__、__DATE__ 和__TIME__ 是字符串文字?这个决定背后的理由是什么(如果有的话)?
【问题讨论】:
-
它比你想象的还要糟糕,它们甚至不是文字......如果它们是你可以例如在构造函数中使用
constexpr函数进行编译时哈希,以获得超级简单、超级便宜的 mini-RTTI 实现(对于序列化来说已经足够好了)。当您尝试时,编译器会告诉您“不是常量表达式”。 -
@Damon 这是一个非常有趣的观察,你有你描述的行为的例子吗?我也想自己测试一下。
-
大约一年前我就尝试过这种方法,因为它似乎是制作 RTTI 系统的好方法,它需要为每个类存储一个静态整数。您需要存储 something,并且构造函数名称(即类名)的编译时散列似乎很理想。函数的名称显然也是一个编译时常量(它实际上并没有太多改变的方式,是吗!)。但 GCC 不喜欢这个想法,因为
__func__类似(小写或大写,漂亮与否)都不是常量表达式。
标签: c++ c-preprocessor standards