如何以对静态代码分析友好的方式使用#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& e)
已更改为 catch (std::exception&)
,因此部署构建失败。
这怎么可能写得不一样?我想到的一个解决方案看起来像这样,但这非常庞大,我想知道这是否可以更优雅地完成:
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++ 中处理开发与部署构建是否过时?
并非如此,但在您的业务逻辑中添加#ifdef
s 并不是一个好习惯。最好将在构建模式之间变化的部分重构为单独的函数,并根据活动的构建模式提供这些函数的不同实现。
这怎么可能写得不一样?
[[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(分析)的传出链接?