C++ 在发布版本中使用assert()时避免未使用的变量警告
有时,局部变量仅用于在assert()中检查它,如下所示-C++ 在发布版本中使用assert()时避免未使用的变量警告,c++,warnings,assertions,C++,Warnings,Assertions,有时,局部变量仅用于在assert()中检查它,如下所示- int Result = Func(); assert( Result == 1 ); 在发布版本中编译代码时,assert()通常是禁用的,因此此代码可能会生成一条关于结果已设置但从未读取的警告 一个可能的解决办法是- int Result = Func(); if ( Result == 1 ) { assert( 0 ); } 但是它需要太多的输入,看起来不容易,并且导致总是检查条件(是的,编译器可能会优化检查,但仍然
int Result = Func();
assert( Result == 1 );
在发布版本中编译代码时,assert()通常是禁用的,因此此代码可能会生成一条关于结果已设置但从未读取的警告
一个可能的解决办法是-
int Result = Func();
if ( Result == 1 )
{
assert( 0 );
}
但是它需要太多的输入,看起来不容易,并且导致总是检查条件(是的,编译器可能会优化检查,但仍然如此)
我正在寻找另一种方式来表达这个assert(),这种方式不会引起警告,但仍然简单易用,并且避免更改assert()的语义
(在此代码区域使用#pragma禁用警告不是一个选项,降低警告级别使其消失也不是一个选项…。这是assert、IMHO的一个错误用法。Assert并不是一个错误报告工具,它是用来断言前提条件的。如果结果未在其他地方使用,则它不是先决条件。您应该将断言移到函数内部返回值之前。您知道返回值不是未引用的局部变量 另外,不管怎么说,在函数内部更有意义,因为它创建了一个具有自己的前置和后置条件的自包含单元 如果函数返回一个值,那么无论如何,您都应该在释放模式下对此返回值进行某种错误检查。因此,它不应该是一个未引用的变量 编辑,但在这种情况下,post条件应为X(请参见注释):
我强烈不同意这一点,应该能够从输入参数确定post条件,如果它是成员函数,则可以确定任何对象状态。如果全局变量修改函数的输出,则应重新构造函数 我们使用宏来明确指示什么时候未使用:
#define _unused(x) ((void)(x))
在您的示例中,您将有:
int Result = Func();
assert( Result == 1 );
_unused( Result ); // make production build happy
这样一来,(a)生产构建成功,(b)代码中很明显,设计没有使用该变量,而不是忘记它。这在不使用函数的参数时特别有用
int Result = Func();
assert( Result == 1 );
Result;
这将使编译器停止抱怨没有使用结果
但是您应该考虑使用assert的一个版本,该版本在运行时执行一些有用的操作,例如将描述性错误记录到可以从生产环境检索的文件中
int Result = Func();
assert( Result == 1 );
这种情况意味着在发布模式下,您确实需要:
Func();
但是Func
是非无效的,即它返回一个结果,即它是一个查询
大概,除了返回一个结果之外,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):您询问了有关命令查询分离原则的参考资料。这本书内容丰富。我在Bertrand Meyer的文章中读到了这种设计技巧
更新(2):j_random_hacker comments“oth,以前返回值的每个“command”函数f()现在必须设置一些变量last_call_to_f_successed或类似”。这仅适用于合同中没有承诺任何内容的函数,即可能“成功”或“失败”的函数或类似概念。使用合约设计,相关数量的函数将具有后条件,因此在“Empty()”之后,对象将是“IsEmpty()”,在“Encode()”之后,消息字符串将是“IsEncoded()”,无需检查。以同样的方式,并且在某种程度上是对称的,在每次调用过程“X()”之前都不调用特殊函数“IsXFeasible()”;因为您通常通过设计知道您在通话时满足了X的先决条件。您可以使用:
Check( Func() == 1 );
并根据需要实现检查(bool)功能。它可以使用assert,也可以抛出特定异常,写入日志文件或控制台,在调试和发布中有不同的实现,或者是所有实现的组合。我将使用以下方法:
#ifdef _DEBUG
#define ASSERT(FUNC, CHECK) assert(FUNC == CHECK)
#else
#define ASSERT(FUNC, CHECK)
#endif
...
ASSERT(Func(), 1);
这样,对于release build,编译器甚至不需要为assert生成任何代码。您可以创建另一个宏,以避免使用临时变量:
#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);
我无法给出比这个更好的答案,解决这个问题,还有更多:
#ifdef NDEBUG
#定义ASSERT(x)do{(void)sizeof(x);}while(0)
#否则
#包括
#定义断言(x)断言(x)
#恩迪夫
当然,您可以使用宏来控制断言定义,例如“\u assert”。因此,您可以这样做:
#ifdef _ASSERT
int Result =
#endif /*_ASSERT */
Func();
assert(Result == 1);
如果此代码在函数中,则执行操作并返回结果:
bool bigPicture() {
//Check the results
bool success = 1 != Func();
assert(success == NO, "Bad times");
//Success is given, so...
actOnIt();
//and
return success;
}
大多数答案建议在
Release
构建中使用static\u cast(expression)
技巧来抑制警告,但如果您的目的是只进行真正的Debug
检查,那么这实际上是次优的。有关断言宏的目标是:
Debug
模式下执行检查Release
模式下不执行任何操作bool bigPicture() {
//Check the results
bool success = 1 != Func();
assert(success == NO, "Bad times");
//Success is given, so...
actOnIt();
//and
return success;
}
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;
}
#ifndef NDEBUG
int Result =
#endif
Func();
assert(Result == 1);
#ifndef NDEBUG
int Result = Func();
assert(Result == 1);
#else
Func();
#endif
// 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);
}
[[maybe_unused]] int Result = Func();
[[maybe_unused]] int Result = Func();
assert( Result == 1 );