代码之家  ›  专栏  ›  技术社区  ›  Hexagon

在发布版本中使用assert()时避免未使用的变量警告

  •  51
  • Hexagon  · 技术社区  · 17 年前

    int Result = Func();
    assert( Result == 1 );
    

    在发布版本中编译代码时,assert()通常是禁用的,因此此代码可能会生成一条关于结果已设置但从未读取的警告。

    一个可能的解决办法是-

    int Result = Func();
    if ( Result == 1 )
    {
        assert( 0 );
    }
    

    我正在寻找另一种方式来表达这个assert(),这种方式不会引起警告,但仍然简单易用,并且避免更改assert()的语义。

    (在此代码区域中使用#pragma禁用警告不是选项,降低警告级别使其消失也不是选项…)。

    16 回复  |  直到 17 年前
        1
  •  58
  •   Jonathan Leffler    10 年前

    我们使用宏来明确指示什么时候未使用:

    #define _unused(x) ((void)(x))
    

    在您的示例中,您将有:

    int Result = Func();
    assert( Result == 1 );
    _unused( Result ); // make production build happy
    

    这样(a)生产构建成功,(b)代码中很明显变量未使用 ,并不是说它只是被遗忘了。这在不使用函数的参数时特别有用。

        2
  •  32
  •   Blaisorblade    5 年前

    我无法给出比这个更好的答案,解决这个问题,还有更多:

    Stupid C++ Tricks: Adventures in assert

    #ifdef NDEBUG
    #define ASSERT(x) do { (void)sizeof(x);} while (0)
    #else
    #include <assert.h>
    #define ASSERT(x) assert(x)
    #endif
    
        3
  •  20
  •   PFee    5 年前

    从C++17开始,变量可以用属性修饰。

    [[maybe_unused]] int Result = Func();
    assert( Result == 1 );
    

    https://en.cppreference.com/w/cpp/language/attributes/maybe_unused 详情请参阅。

    这比这个好 (void)Result

        4
  •  9
  •   Adam Rosenfield    17 年前

    您可以创建另一个宏,以避免使用临时变量:

    #ifndef NDEBUG
    #define Verify(x) assert(x)
    #else
    #define Verify(x) ((void)(x))
    #endif
    
    // asserts that Func()==1 in debug mode, or calls Func() and ignores return
    // value in release mode (any braindead compiler can optimize away the comparison
    // whose result isn't used, and the cast to void suppresses the warning)
    Verify(Func() == 1);
    
        5
  •  8
  •   Daniel Daranas    17 年前
    int Result = Func();
    assert( Result == 1 );
    

    这种情况意味着在发布模式下,您确实需要:

    Func();
    

    但是 Func 为非void,即返回结果,即为 查询

    Func 修改某些内容(否则,为什么要麻烦调用它而不使用它的结果呢?),即它是一个 命令 .

    由 (1), Func 不应同时是命令和查询。换句话说,查询不应该有副作用,命令的“结果”应该由对象状态的可用查询表示。

    Cloth c;
    c.Wash(); // Wash is void
    assert(c.IsClean());
    

    比

    Cloth c;
    bool is_clean = c.Wash(); // Wash returns a bool
    assert(is_clean);
    

    所以,简而言之,我的答案是:不要这样写代码:)

    更新(1): 你要的是关于这个项目的推荐人 命令查询分离原则 . Wikipedia 这本书内容丰富。我在一本书中读到了这种设计技巧 Object Oriented Software Construction, 2nd Editon 伯特兰·迈耶。

    更新(2): 合同设计 ,将具有相关数量的功能 ,因此在“Empty()”之后,对象将是“IsEmpty()”,而在“Encode()”之后,消息字符串将是“IsEncoded()”,无需检查。以同样的方式,并且在某种程度上是对称的,在每次调用过程“X()”之前都不调用特殊函数“IsXFeasible()”;因为你通常通过设计知道你在调用时满足了X的先决条件。

        6
  •  4
  •   Jem    17 年前

    您可以使用:

    Check( Func() == 1 );
    

    并根据需要实现检查(bool)功能。它可以使用assert,也可以抛出特定的异常,写入日志文件或控制台,在debug和release中有不同的实现,或者所有这些的组合。

        7
  •  3
  •   Aelian    7 年前

    使用C++17,我们可以做到:

    [[maybe_unused]] int Result = Func();
    

    尽管与断言替换相比,它需要一些额外的类型。看见 this answer .

        8
  •  2
  •   anon anon    17 年前

    这是assert的一个错误用法,IMHO。Assert并不是一个错误报告工具,它是用来断言前提条件的。若结果并没有在其他地方使用,那个么它并不是一个先决条件。

        9
  •  2
  •   Brian R. Bondy    17 年前

    另外,不管怎么说,在函数内部更有意义,因为它创建了一个具有自己的前置和后置条件的自包含单元。

    如果函数返回一个值,那么无论如何,您都应该在释放模式下对此返回值进行某种错误检查。因此,它不应该是一个未引用的变量。

    编辑,但在这种情况下,post条件应为X(请参见注释):

    我强烈不同意这一点,应该能够从输入参数确定post条件,如果它是成员函数,则可以确定任何对象状态。如果全局变量修改函数的输出,则应重新构造函数。

        10
  •  2
  •   Sergey Nikitin    10 年前

    static_cast<void>(expression) 欺骗 Release 生成以抑制警告,但如果您的目的是进行真正的检查,那么这实际上是次优的 Debug

    1. 办理登机手续 调试 模式
    2. 无所事事 释放
    3. 在所有情况下都不发出警告

    评价的 . 例如,如果你只是做一个变量检查,那可能没什么大不了的。但是如果在断言检查中调用某个函数,比如 ASSERT(fetchSomeData() == data); fetchSomeData()

    你真正需要的不仅仅是警告,更重要的是- 非评价 仅调试检查表达式的。这可以通过我从一个专门的 Assert 图书馆:

    void myAssertion(bool checkSuccessful)
    {
       if (!checkSuccessful)
        {
          // debug break, log or what not
        }
    }
    
    #define DONT_EVALUATE(expression)                                    \
       {                                                                 \
          true ? static_cast<void>(0) : static_cast<void>((expression)); \
       }
    
    #ifdef DEBUG
    #  define ASSERT(expression) myAssertion((expression))
    #else
    #  define ASSERT(expression) DONT_EVALUATE((expression))
    #endif // DEBUG
    
    int main()
    {
      int a = 0;
      ASSERT(a == 1);
      ASSERT(performAHeavyVerification());
    
      return 0;
    }
    

    DONT_EVALUATE 表达式中不需要对表达式进行求值。为了加强这一点,C++标准保证只有条件操作符的一个分支将被评估。以下是报价:

    5.16条件运算符[expr.cond]

    逻辑还是表达式?表达式:赋值表达式

    上下文转换为bool。对其进行评估,如果为真,则 条件表达式的结果是第二个表达式的值 表达式,否则为第三个表达式的表达式。只有一个 表达式被计算。

    释放 构建(事实上,整个过程完全优化了)。请记住,使用这种方法,如果将具有副作用的表达式放在断言宏中,那么很快就会遇到麻烦,尽管这首先是一种非常糟糕的做法。

    此外,我希望静态分析器可以警告使用这种方法的“表达式的结果总是恒定的”(或类似的东西)。我已经用clang、VS2013、VS2015静态分析工具对此进行了测试,但没有收到类似的警告。

        11
  •  2
  •   andybuckley    9 年前

    NDEBUG 宏是专门定义的,如果断言 不会 (那样做)仅仅是因为 -DNDEBUG

    #ifndef NDEBUG
    int Result =
    #endif
    Func();
    assert(Result == 1);
    

    #ifndef NDEBUG
    int Result = Func();
    assert(Result == 1);
    #else
    Func();
    #endif
    

    一般来说,使用这些东西时,要小心,不可能用不同的语言构建不同的翻译单元 宏观状态——特别是re。公共头文件中的断言或其他条件内容。危险在于,您或库的用户可能会意外地实例化与库的编译部分中使用的内联函数定义不同的内联函数定义,从而悄悄地违反 one definition rule

        12
  •  1
  •   user1308676 user1308676    14 年前

    当然,您可以使用宏来控制断言定义,例如“\u assert”。因此,您可以这样做:

    #ifdef _ASSERT 
    int Result =
    #endif /*_ASSERT */
    Func();
    assert(Result == 1);
    
        13
  •  0
  •   John Dibling    17 年前
    int Result = Func();
    assert( Result == 1 );
    Result;
    

    但是您应该考虑使用assert的一个版本,该版本在运行时执行一些有用的操作,例如将描述性错误记录到可以从生产环境检索的文件中。

        14
  •  0
  •   Donotalo    17 年前

    我会使用以下方法:

    #ifdef _DEBUG
    #define ASSERT(FUNC, CHECK) assert(FUNC == CHECK)
    #else
    #define ASSERT(FUNC, CHECK)
    #endif
    
    ...
    
    ASSERT(Func(), 1);
    

        15
  •  0
  •   Michael T    11 年前

    如果此代码在函数中,则执行操作并返回结果:

    bool bigPicture() {
    
       //Check the results
       bool success = 1 != Func();
       assert(success == NO, "Bad times");
    
       //Success is given, so...
       actOnIt();
    
       //and
       return success;
    }
    
        16
  •  0
  •   Trade-Ideas Philip    8 年前
    // Value is always computed.  We also call assert(value) if assertions are
    // enabled.  Value is discarded either way.  You do not get a warning either
    // way.  This is useful when (a) a function has a side effect (b) the function
    // returns true on success, and (c) failure seems unlikely, but we still want
    // to check sometimes.
    template < class T >
    void assertTrue(T const &value)
    {
      assert(value);
    }
    
    template < class T >
    void assertFalse(T const &value)
    { 
      assert(!value);
    }