如何以对静态代码分析友好的方式使用#define 守卫?

Posted

技术标签:

【中文标题】如何以对静态代码分析友好的方式使用#define 守卫?【英文标题】:How to use #define guards in a static code analysis-friendly way? 【发布时间】:2020-02-13 09:30:35 【问题描述】:

我们目前使用#ifdef DEPLOYED 来识别我们的 c++17 项目的部署版本并更改行为。

但是,静态代码分析,例如在 IDE 内部经常(乍一看是正确的)警告未使用的变量,过去我草率地删除了这些变量,只是为了后来在我们的夜间发现构建的部署失败(因为在我们的 CI 中,测试是在非-deploy 构建,仅在打开标志的情况下进行夜间部署构建)。

众多示例之一是:

catch (std::exception& e) 
#if defined(DEPLOYED)
    std::cerr << "Exception doing something " << e.what() << std::endl;
#else
    throw;
#endif

在开发人员机器上,从未使用过 e,因此出现了警告,catch (std::exception&amp; e) 已更改为 catch (std::exception&amp;) ,因此部署构建失败。

这怎么可能写得不一样?我想到的一个解决方案看起来像这样,但这非常庞大,我想知道这是否可以更优雅地完成:

catch (std::exception& e) 
    if (
#if defined(DEPLOYED)
       true
#else
       false
#endif
    ) 
        std::cerr << "Exception doing something " << e.what() << std::endl;
     else 
        throw;
    
#endif

另一个例子包括一个变量重命名,它在除#if defined(DEPLOYED) 代码块之外的所有地方(IDE 支持“替换所有匹配项”)都完成。

使用define 在 C++ 中处理开发与部署构建是否过时?如果是,推荐的方式是什么? 尤其是当非部署代码可能包含计算量大的调用时,我如何确保它们不会出现在部署构建中 - 甚至可以完全优化掉?

【问题讨论】:

解决此类问题的另一种“老派”方法是在 catch 正文的第一行添加 (void)e;。如果您出于某种原因无法使用[[maybe_unused]],则很有用(尽管使用 C++17,这应该不是问题)。 【参考方案1】:

使用define 在 C++ 中处理开发与部署构建是否过时?

并非如此,但在您的业务逻辑中添加#ifdefs 并不是一个好习惯。最好将在构建模式之间变化的部分重构为单独的函数,并根据活动的构建模式提供这些函数的不同实现。

这怎么可能写得不一样?

[[maybe_unused]] 属性优雅地解决了这个问题。

catch ([[maybe_unused]] std::exception& e) 
#if defined(DEPLOYED)
    std::cerr << "Exception doing something " << e.what() << std::endl;
#else
    throw;
#endif

【讨论】:

【参考方案2】:

使用 C++ 处理开发与部署构建是否过时 定义?如果可以,推荐的方式是什么?

使用构建系统来驱动它可能会更好。然后,构建系统可以根据它是部署还是调试构建来拉入正确的文件/方法

你的代码可能看起来像

catch (std::exception& e) 

    HandleError(e); 

其中 HandleError 在部署构建中只是记录但在调试中重新抛出 e。

特别是当非部署代码可能包含计算 昂贵的电话,我如何确保它们不会出现在部署中 构建 - 甚至可以完全优化?

这在使用构建系统时得到保证,因为永远不会编译不需要的文件

【讨论】:

【参考方案3】:

一些静态分析器工具(尤其是Frama-C 和可能的Clang static analyzer)正在处理预处理的 C++ 形式。您可以考虑在命令行上使用它们(并通过您的 build automation 工具驱动它们,例如您的 Makefile

您还可以(通过数周或数月的工作)为您的GCC plugin 编码,以便与您最喜欢的静态分析器或证明助手进行交互。如需灵感,请查看Bismon。

【讨论】:

以上是关于如何以对静态代码分析友好的方式使用#define 守卫?的主要内容,如果未能解决你的问题,请参考以下文章

如何以准确和用户友好的方式计算与Google Analytics(分析)的传出链接?

java中啥是友好变量和友好方法

如何使我的代码生成令牌并将其返回以对用户进行身份验证?

3D打印Marlin2.0固件源代码分析之如何使用LOG接口调试代码

如何引用一个已经定义过的全局变量

静态代码分析工具sonarqube+sonar-runner的安装配置及使用