【问题标题】:Enforce a condition check before C code blocks在 C 代码块之前强制执行条件检查
【发布时间】:2014-05-22 19:46:44
【问题描述】:

简短的故事:我想在某些条件编译的代码块中强制执行运行时条件检查,而不是在其他代码块中。

长篇大论: 我们的 C 代码库严重依赖分析。 我们有很多不同的配置文件,几乎每个模块都有#ifdefs,用于至少 5-6 个不同的配置文件,包装大段代码。

我们添加了一项新要求 - 对于某些配置文件(例如 <A><B>),只有在满足特定条件时才会运行条件编译代码。对于其他配置文件(比如<C>),这不是必需的。

也就是说,这就是代码现在的样子:

#ifdef <A>
    /* Profiled code… */
#endif /* <A> */

#ifdef <B>
    /* Profiled code… */
#endif /* <B> */

#ifdef <C>
    /* Profiled code… */
#endif /* <C> */

这就是我们需要的样子:

#ifdef <A>
    if (new_condition)
    {
        /* Profiled code… */
    }
#endif /* <A> */

#ifdef <B>
    if (new_condition)
    {
        /* Profiled code… */
    }
#endif /* <B> */

#ifdef <C>
    /* Profiled code… */
#endif /* <C> */

到目前为止,一切都很好。

但是,通过上述实现,新要求实际上是不可维护的。 它不会阻止任何不熟悉添加新分析代码块的要求的人,而无需添加对 new_condition 的检查。

因此,我想强制我们的工程师在添加在&lt;A&gt;&lt;B&gt; 下配置的新块时检查这种情况,而不是在&lt;C&gt; 下配置的代码。

理论上,如果存在#ifdef &lt;A&gt;#ifdef &lt;B&gt; 不直接跟在if (new_condition) 之后,那么最好的解决方案可能是使构建过程失败。

问题是,我不知道这样做。你们有吗?

【问题讨论】:

  • A、B 和 C 可以存在于同一个程序中还是属于同一个“编译器开关”?
  • 它们都可以存在于同一个程序中,是的。大多数时候每次出现几次,有时又相互嵌套。它们可以包装大块代码,或者只是一个命令,或者介于两者之间的任何东西。

标签: c c-preprocessor conditional-compilation


【解决方案1】:

我有理由确定它不能按照描述的那样完成。您的工程师似乎可以编写从 #ifdef 到 #endif 的所有代码,而您拥有的唯一“钩子”是条件符号和条件。这还不够。一些半随机的想法:

  • 您可以将“条件”转换为函数调用并计算它被调用的频率。
  • 当所有条件都设置为 false 时,您可以要求某些“空行为”。
  • 您可以创建一些函数,您的工程师必须在其块中调用才能访问某些内容,并在运行时检查如果条件为假,它不会被调用。

最终,我认为您最好使用源代码分析工具来分析源代码并报告编码风格的违规行为。那将是 CppCheck 或其中之一:http://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis

【讨论】:

    【解决方案2】:

    有点难以回答,因为一切都很模糊 :) 但我建议使用某种包装器,不允许编辑代码的人更改它:

    typedef void(*stuff_ptr)(void);
    
    inline void do_stuff (stuff_ptr stuff)
    {
      #if defined(A) || defined(B)
      if (!new_condition)
      {
        return ;
      }
      #endif
    
      stuff();
    }
    
    #ifdef A
      do_stuff(A_stuff);
    #endif
    
    #ifdef B
      do_stuff(B_stuff);
    #endif
    
    #ifdef C
      do_stuff(C_stuff);
    #endif
    

    这不会导致构建失败,而是首先防止不正确的构建发生。

    do_stuff() 可以根据需要实现为类似函数的宏,但如果可以,请避免这样做。如果需要,“new_condition”可以作为另一个参数传递。

    【讨论】:

    • 这是个好主意,但不幸的是它不适用于我的情况。该解决方案应该适用于任意分析代码,而不是特定函数。
    • @user2776936 如果你用类似函数的宏替换内联函数,用另一个类似函数的宏替换函数指针,它将适用于任意代码。
    【解决方案3】:

    保持当前开始宏、结束宏样式块的一个想法可能类似于以下内容:

    #include <stdio.h>
    
    #define FOO 1
    #define BAZ 1
    
    int condition_a = 1;
    int condition_b = 0;
    
    #ifdef FOO
    #  define FOO_BEGIN if (condition_a) {
    #  define FOO_END }
    #else
    #  define FOO_BEGIN if (0) {
    #  define FOO_END }
    #endif
    
    #ifdef BAR
    #  define BAR_BEGIN if (condition_b) {
    #  define BAR_END }
    #else
    #  define BAR_BEGIN if (0) {
    #  define BAR_END }
    #endif
    
    #ifdef BAZ
    #  define BAZ_BEGIN if (1) {
    #  define BAZ_END }
    #else
    #  define BAZ_BEGIN if (0) {
    #  define BAZ_END }
    #endif
    
    int main (int argc, char ** argv)
    {
        FOO_BEGIN
            printf("in foo\n");
        FOO_END
    
        BAR_BEGIN
            printf("in bar\n");
        BAR_END
    
        BAZ_BEGIN
            printf("in baz\n");
        BAZ_END
    }
    

    打印

    in foo
    in baz
    

    您将不得不更新定义进入和退出块的宏,或者需要这些 ifdef 中的某些内容。

    【讨论】:

    • 这可能是最好的解决方案。它的问题是它迫使我们更改代码库中的每个#ifdef 调用,这会导致交付时非常头疼。另一个问题是它仍然允许编码人员使用#ifdefs,就像他们习惯的那样。我想我们只需要教育他们:)
    猜你喜欢
    • 2021-10-02
    • 2019-02-07
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 2020-10-01
    • 2021-04-21
    • 2021-03-09
    • 1970-01-01
    相关资源
    最近更新 更多