Explanation for assert macro
I couldn't comment on the answer itself, so: about Using comma to prevent the need for brace pair
#define MY_ASSERT(expr) ((expr) || (debugbreak(), 0))
Here debugbreak() returns void, but we still wish to have 0 as an rvalue.
How does (debugbreak(), 0) work to return 0? I understand that the return value of debugbreak() is discarded and 0 is returned, but debugbreak generates an exception, so how can anything be evaluated after开发者_StackOverflowward? I suppose my question can be generalised to any similar binary operator where the first part being evaluated exits the program.
It's a type system hack.
#define MY_ASSERT(expr) ((expr) || (debugbreak(), 0))
// note the or operator here ^
The ||
operator takes two bool
-typed (or convertible) expressions. debugbreak()
is void
-typed. To make it bool
, use the following rule:
(FOO, BAR)
// ^ determines the type of the entire comma expression
This is the same as {FOO; BAR}
except that a block (in braces) has no type.
Nothing will be evaluated if the assert fires, but both expressions must have the right return type, otherwise this macro will just break compilation.
The basic idea is pretty simple. He's trying to get an effect pretty much the same as if he'd written: if (!expr) debugbreak();
. For a macro, however, he wants that as a single expression. To do that, he's using ||
, which evaluates its left argument, then if and only if that is false, evaluates the right argument -- then produces an overall result that is the logical OR of the two operands.
In this case, he doesn't really care about that logical OR that's produced as a result; he just wants the evaluate the left, then if it's false evaluate the right. The compiler, however does care -- in particular, it demands that both operands of ||
have some type that can be converted to bool (or, in C, 0 or 1).
To give the compiler that, he uses the comma operator, which evaluates its left operand, then its right operand and produces the value of the right operand as the result. That lets him get the debugbreak()
evaluated, while the 0
keeps the compiler happy by giving it an int as the result value, which it can then OR with whatever value was produced by the expr
to produce the result of the overall expression (which, of course, is irrelevant and almost certain to be ignored).
I think what your all missing is the fact that ( A || B ) follows short circuiting rules.
If A is true then there is NO NEED to evaluate B. Since B does not need to be evaluated DebugBreak() never gets called.
If A is false, then we HAVE to evaluate B to determine the output of ( A || B ). There is no short circuit. (DebugBreak(), 0) of more Appropriately (DebugBreak(), false)
http://www.student.cs.uwaterloo.ca/~cs132/Weekly/W02/SCBooleans.html
http://msdn.microsoft.com/en-us/library/zs06xbxh(VS.80).aspx
精彩评论