用预处理器替换 C++ 类/静态方法?
Posted
技术标签:
【中文标题】用预处理器替换 C++ 类/静态方法?【英文标题】:Replace C++ class/static method with preprocessor? 【发布时间】:2021-12-08 10:13:01 【问题描述】:我想使用内置编译器检查来验证自定义日志框架的格式字符串,以提前捕获由于格式字符串参数不匹配而导致的奇怪运行时崩溃。
自定义 C++ 日志记录方法的参数与 printf()
系列相同,因此我试图替换所有调用
MyLogger::Error(
与
fprintf(stderr,
虽然不幸的是 (clang) 预处理器在作用域解析运算符 (::
) 上阻塞,即仅识别 ULog
子字符串而不是 ULog::Warn(
:
#define MyLogger::Error( fprintf(stderr,
非常感谢您对如何完成这项工作的任何建议。
【问题讨论】:
预处理器宏中不允许使用冒号。您是否考虑过使用constexpr
函数?它们在编译时执行,您可以在不受预处理器限制的情况下获得语言的全部功能。
如果您使用 GCC 或 Clang,那么您可以添加函数属性来检查 printf
之类的字符串及其参数 搜索例如gcc attribute printf
.
@Someprogrammerdude 我不知道我以前怎么没听说过。不错。
为什么不使用sed
? sed "s/MyLogger::Error(/fprintf(stderr,/" < source.cpp > source_modified.cpp
注意:一种可移植的方式是使用 C++ 属性语法,在其他平台上应该忽略它:[[gnu::format (printf, 1, 2)]]
on godbolt
【参考方案1】:
如果将MyLogger::Error
修改为
MyLogger::Error(args)
if (0)
fprintf(stderr,args)
//actual function
这样您可以获得内置警告,并且不会影响代码的效率。 (如果你想写到stderr,你显然可以实际使用打印,但我想如果你想要你已经使用过)
【讨论】:
问题是格式字符串在这个函数体中是可变的,所以你不会得到内置的警告 @BenVoigt 你是对的。【参考方案2】:您是否尝试过可变参数模板?找到here。
#include <iostream>
namespace MyLogger
template <typename... T>
auto Error(const char * _Format, T &&... args)
return printf(_Format, std::forward<T>(args)...);
;
#define printf(...) MyLogger::Error(__VA_ARGS__)
int main()
MyLogger::Error("Non-Macro Print \n");
printf("Macro Print \n");
return 0;
【讨论】:
【参考方案3】:详细说明 @Someprogrammerdude 建议的方法 我已经扩展了自定义日志记录类以使用 clang/gcc format 属性来启用编译器格式检查。
声明就变成了
static void Error(const char *format,...) __attribute__ ((format (printf, 1, 2)));
使用预处理器进行临时处理比原来的想法更好。通过将自定义格式化程序的调用替换为对 printf()
的调用来启用检查,因为它始终启用,立即捕获参数不匹配!
(FWIW - 已经在我们的 120 多个 LOC 代码库上修复了数十个问题和几个潜在的崩溃)
【讨论】:
120+ LOC,似乎不多。 LOL - 应该说 120k ;-)以上是关于用预处理器替换 C++ 类/静态方法?的主要内容,如果未能解决你的问题,请参考以下文章