用预处理器替换 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 我不知道我以前怎么没听说过。不错。 为什么不使用sedsed "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++ 类/静态方法?的主要内容,如果未能解决你的问题,请参考以下文章

针对 C++ 静态破坏/构造顺序问题的特定于平台的解决方法

静态属性和静态方法

静态方法的 Xcode 中的 C++ 链接器错误

Python学习-08-面向对象编程进阶和异常处理

静态方法怎么调用

什么是静态方法?