自定义 C++ 断言宏
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5252375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Custom C++ assert macro
提问by Steven Lu
I stumbled upon an informative article: http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/which pointed out a great number of problems that exist in my current suite of debugging macros.
我偶然发现了一篇内容丰富的文章:http: //cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/,它指出了我当前的调试宏套件中存在的大量问题。
The full code for the final version of the macro is given near the end of the article if you follow the link.
如果您点击链接,则在文章末尾附近给出了宏最终版本的完整代码。
The general form as presented is like this (somebody please correct me if i am wrong in transposing it):
呈现的一般形式是这样的(如果我在转置它时有错误,请有人纠正我):
#ifdef DEBUG
#define ASSERT(cond) \
do \
{ \
if (!(cond)) \
{ \
ReportFailure(#cond, __FILE__, __LINE__, 0); \
HALT(); \
} \
} while(0)
#else
#define ASSERT(cond) \
do { (void)sizeof(cond); } while(0)
While thinking about modifying my code with what I have learned, I noticed a couple of interesting variations posted in the comments for that article:
在考虑用我学到的知识修改我的代码时,我注意到该文章的评论中发布了一些有趣的变化:
One was that you cannot use this macro with the ternary operator (i.e. cond?ASSERT(x):func()
), and the suggestion was to replace the if()
with the ternary operator and some parentheses as well as the comma operator. Later on another commenter provided this:
一个是您不能将此宏与三元运算符(即cond?ASSERT(x):func()
)一起使用,建议将 替换if()
为三元运算符和一些括号以及逗号运算符。后来另一位评论者提供了这个:
#ifdef DEBUG
#define ASSERT(x) ((void)(!(x) && assert_handler(#x, __FILE__, __LINE__) && (HALT(), 1)))
#else
#define ASSERT(x) ((void)sizeof(x))
#endif
I thought the use of logical and &&
is particularly smart in this case and it seems to me that this version is more flexible than one using the if
or even the ternary ?:
. Even nicer is that the return value of assert_handler
can be used to determine if the program should halt. Although I am not sure why it is (HALT(), 1)
instead of just HALT()
.
我认为&&
在这种情况下使用逻辑和特别聪明,在我看来,这个版本比使用if
甚至三元的版本更灵活?:
。更好的是,assert_handler
可以使用的返回值来确定程序是否应该停止。虽然我不确定为什么它是(HALT(), 1)
而不是HALT()
.
Are there any particular shortcomings with the second version here that I have overlooked? It does away with the do{ } while(0)
wrapped around the macros but it seems to be unnecessary here because we don't need to deal with if
s.
我忽略了这里的第二个版本有什么特别的缺点吗?它消除了do{ } while(0)
环绕宏的包装,但在这里似乎没有必要,因为我们不需要处理if
s。
What do you think?
你怎么认为?
回答by AnT
In C and C++ standard library, assert
is a macro that is required to act as a function. A part of that requirement is that users must be able to use it in expressions. For example, with standard assert
I can do
在 C 和 C++ 标准库中,assert
是一个需要充当函数的宏。该要求的一部分是用户必须能够在表达式中使用它。例如,使用标准assert
我可以做
int sum = (assert(a > 0), a) + (assert(b < 0), b);
which is functionally the same as
在功能上与
assert(a > 0 && b < 0)
int sum = a + b;
Even though the former might not be a very good way to write an expression, the trick is still very useful in many more appropriate cases.
尽管前者可能不是编写表达式的好方法,但该技巧在许多更合适的情况下仍然非常有用。
This immediately means that if one wants their own custom ASSERT
macro to mimic standard assert
behavior and usability, then using if
or do { } while (0)
in the definition of ASSERT
is out of question. One is limited to expressions in that case, meaning using either ?:
operator or short-circuiting logical operators.
这立即意味着,如果您希望自己的自定义ASSERT
宏模仿标准assert
行为和可用性,那么在 的定义中使用if
或是不可能的。一种仅限于这种情况下的表达式,这意味着使用运算符或短路逻辑运算符。do { } while (0)
ASSERT
?:
Of course, if one doesn't care about making a standard-like custom ASSERT
, then one can use anything, including if
. The linked article doesn't even seem to consider this issue, which is rather strange. In my opinion, a function-like assert macro is definitely more useful than a non-function-like one.
当然,如果一个人不关心制作一个标准的 custom ASSERT
,那么你可以使用任何东西,包括if
. 链接的文章似乎甚至没有考虑这个问题,这很奇怪。在我看来,类似函数的 assert 宏肯定比非类似函数的宏更有用。
As for the (HALT(), 1)
... It is done that way because &&
operator requires a valid argument. The return value of HALT()
might not represent a valid argument for &&
. It could be void
for what I know, which means that a mere HALT()
simply won't compile as an argument of &&
. The (HALT(), 1)
always evaluates to 1
and has type int
, which is always a valid argument for &&
. So, (HALT(), 1)
is always a valid argument for &&
regardless of the type of HALT()
.
至于(HALT(), 1)
... 这样做是因为&&
操作符需要一个有效的参数。的返回值HALT()
可能不代表 的有效参数&&
。这可能是void
我所知道的,这意味着仅仅HALT()
不会编译为&&
. 该(HALT(), 1)
结果始终为1
,具有类型int
,这始终是一个有效的论据&&
。因此,(HALT(), 1)
始终是一个有效的参数&&
不管类型HALT()
。
Your last comment about do{ } while(0)
does not seem to make much sense. The point of enclosing a macro into do{ } while(0)
is to deal with external if
s, not the if
s inside the macro definition. You alwayshave to deal with external if
s, since there's always a chance that your macro will be used in an external if
. In the latter definition do{ } while(0)
is not needed because that macro is an expression. And being an expression, it already naturally has no problems with external if
s. So, there's no need to do anything about them. Moreover, as I said above, enclosing it into do{ } while(0)
would completely defeat its purpose, turning it into a non-expression.
你最后的评论do{ } while(0)
似乎没有多大意义。将宏封装到其中的目的do{ } while(0)
是处理外部if
s,而不是if
宏定义中的s。您总是必须处理 external if
s,因为您的宏总是有可能在 external 中使用if
。在后者中do{ } while(0)
不需要定义,因为该宏是一个表达式。而且作为一个表达式,已经自然没有外部if
s的问题了。所以,没有必要对它们做任何事情。而且,正如我上面所说的,将它封闭起来do{ } while(0)
会完全违背它的目的,将它变成一个非表达式。
回答by Gregory Pakosz
For the sake of completeness, I published a drop-in 2 files assert macro implementation in C++:
为了完整起见,我在 C++ 中发布了一个插入的 2 个文件 assert 宏实现:
#include <pempek_assert.h>
int main()
{
float min = 0.0f;
float max = 1.0f;
float v = 2.0f;
PEMPEK_ASSERT(v > min && v < max,
"invalid value: %f, must be between %f and %f", v, min, max);
return 0;
}
Will prompt you with:
将提示您:
Assertion 'v > min && v < max' failed (DEBUG)
in file e.cpp, line 8
function: int main()
with message: invalid value: 2.000000, must be between 0.000000 and 1.000000
Press (I)gnore / Ignore (F)orever / Ignore (A)ll / (D)ebug / A(b)ort:
Where
在哪里
- (I)gnore: ignore the current assertion
- Ignore (F)orever: remember the file and line where the assertion fired and ignore it for the remaining execution of the program
- Ignore (A)ll: ignore all remaining assertions (all files and lines)
- (D)ebug: break into the debugger if attached, otherwise
abort()
(on Windows, the system will prompt the user to attach a debugger) - A(b)ort: call
abort()
immediately
- (I)gnore:忽略当前断言
- Ignore (F)orever:记住断言触发的文件和行,并在程序的剩余执行过程中忽略它
- Ignore (A)ll:忽略所有剩余的断言(所有文件和行)
- (D)ebug:如果附加了就闯入调试器,否则
abort()
(在Windows上,系统会提示用户附加调试器) - A(b)ort:
abort()
立即调用
You can find out more about it there:
你可以在那里找到更多关于它的信息:
Hope that helps.
希望有帮助。
回答by Chris Lutz
Although I am not sure why it is
(HALT(), 1)
instead of justHALT()
.
虽然我不确定为什么它是
(HALT(), 1)
而不是HALT()
.
I imagine HALT
may be a macro (or other stand-in name) for exit
. Say we wanted to use exit(1)
for our HALT
command. exit
returns void
, which cannot be evaluated as the second argument to &&
. If you use the comma operator, which evaluates its first argument, then evaluates and returns the value of it's second argument, we have an integer (1) to return to &&
, even though we never reach that point because HALT()
will cause us to stop long before then.
我想HALT
可能是exit
. 假设我们想exit(1)
用于我们的HALT
命令。exit
返回void
,不能作为 的第二个参数计算&&
。如果您使用逗号运算符,它计算第一个参数,然后计算并返回它的第二个参数的值,我们有一个整数 (1) 返回到&&
,即使我们从未到达那个点,因为这HALT()
会导致我们很久之前停止然后。
Basically, any function that fills in for HALT
is probably going to have a return value of void
, because it would make no sense for it to return any value. We couldmake it return an int
, just for the sake of the macro, but if we're already hacking around with a macro a little more hackery can't hurt, can it?
基本上,任何填入 for 的函数HALT
都可能有一个返回值void
,因为它返回任何值都是没有意义的。我们可以让它返回一个int
,只是为了宏,但是如果我们已经在用宏来攻击,那么更多的黑客就不会受到伤害,不是吗?