【问题标题】:Replace C++ class/static method with preprocessor?用预处理器替换 C++ 类/静态方法?
【发布时间】: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

标签: c++ c++11 gcc c++17 c-preprocessor


【解决方案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 ;-)
    猜你喜欢
    • 2021-11-21
    • 1970-01-01
    • 2017-08-08
    • 2019-06-27
    • 2014-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多