我想了解这个断言宏
I'd like to understand this assert macro
在Visual Studio中,断言宏定义如下:
#define assert(expression) (void)( \
(!!(expression)) || \
(_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
)
我在 || 之后得到了正确的部分仅在左侧表达式为假时才求值,并抛出异常。
我有 3 个问题:
!!双重否定是把'expression'类型转成bool吧?如果他们被排除在外会发生什么? '表达式在与另一个表达式进行逻辑或运算时会隐式转换为布尔值,对吗?在:
浮动()|| /* 调用抛出的函数*/;
float() 无论如何都转换为 bool 类型?
2.After ||我们有运算符:
( /* call function that throws */ , 0 )
逗号是一个序列点,因此首先调用函数,然后将此表达式计算为 0。如果函数调用没有终止程序,则表达式将简单地为:
!!(expression)) || 0
逗号后面的 0 有什么意义?如果不存在,则表达式为:
!!(expression)) || / * call function that throws */
这是一回事吧?
- 最后是 (void),我想知道这是干什么用的。
让我们分解一下代码:
#define assert(expression) (void)( \
(!!(expression)) || \
(_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
)
(void)
是为了消除编译器警告表达式结果未被使用。并且还确保断言不会被意外用作更大表达式的一部分。
!!(expression)
的 !!
部分是为了确保表达式的计算结果为 false
或 true
.
但是恶作剧的 C++ 代码可能会覆盖 operator!
并在此处造成一些混乱。
为什么是 C 风格的转换,而不是 static_cast<bool>
?我怀疑同一个头文件用于支持 C 和 C++,也可能用于支持 C++/CLI。 (如果是这样,可能有理由在两种语言特定的宏中对 C++ 使用 static_cast<bool>
,对 C 使用 (bool)
。)
如果 expression
对象有一个 operator bool
转换,它将被 !!
使用。 (但也会被 (bool)(expression)
使用。)
无论如何,!!
不必要的观察对我来说似乎是正确的。 C++ 中 truthy(非零)或 falsy(零)的表达式是语言的一部分。
使用 (x(), 0)
逗号运算符是因为 x
(在本例中为 _wassert
)returns 无效。如果 b
为空,则不能执行 a || b
,所以这个技巧只是为了调用 void,并且表达式导致 int
。没关系是0
.
_wassert
不抛出。它可能会以退出代码 3 中止。它可能会进入调试器。
可以忽略并继续。如果断言被违反,可能会导致断轴后果,或者代码可能有一些防御性编程来处理断言违规。
由于 assert 类函数宏被编写为 表达式,它巧妙地避免了“悬空 if
宏”的问题(有一个 if (!(expression)) ...
构造被使用),并且是 表达式 而不是 语句 .
在Visual Studio中,断言宏定义如下:
#define assert(expression) (void)( \
(!!(expression)) || \
(_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
)
我在 || 之后得到了正确的部分仅在左侧表达式为假时才求值,并抛出异常。
我有 3 个问题:
!!双重否定是把'expression'类型转成bool吧?如果他们被排除在外会发生什么? '表达式在与另一个表达式进行逻辑或运算时会隐式转换为布尔值,对吗?在:
浮动()|| /* 调用抛出的函数*/;
float() 无论如何都转换为 bool 类型?
2.After ||我们有运算符:
( /* call function that throws */ , 0 )
逗号是一个序列点,因此首先调用函数,然后将此表达式计算为 0。如果函数调用没有终止程序,则表达式将简单地为:
!!(expression)) || 0
逗号后面的 0 有什么意义?如果不存在,则表达式为:
!!(expression)) || / * call function that throws */
这是一回事吧?
- 最后是 (void),我想知道这是干什么用的。
让我们分解一下代码:
#define assert(expression) (void)( \
(!!(expression)) || \
(_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
)
(void)
是为了消除编译器警告表达式结果未被使用。并且还确保断言不会被意外用作更大表达式的一部分。
!!(expression)
的 !!
部分是为了确保表达式的计算结果为 false
或 true
.
但是恶作剧的 C++ 代码可能会覆盖 operator!
并在此处造成一些混乱。
为什么是 C 风格的转换,而不是 static_cast<bool>
?我怀疑同一个头文件用于支持 C 和 C++,也可能用于支持 C++/CLI。 (如果是这样,可能有理由在两种语言特定的宏中对 C++ 使用 static_cast<bool>
,对 C 使用 (bool)
。)
如果 expression
对象有一个 operator bool
转换,它将被 !!
使用。 (但也会被 (bool)(expression)
使用。)
无论如何,!!
不必要的观察对我来说似乎是正确的。 C++ 中 truthy(非零)或 falsy(零)的表达式是语言的一部分。
使用 (x(), 0)
逗号运算符是因为 x
(在本例中为 _wassert
)returns 无效。如果 b
为空,则不能执行 a || b
,所以这个技巧只是为了调用 void,并且表达式导致 int
。没关系是0
.
_wassert
不抛出。它可能会以退出代码 3 中止。它可能会进入调试器。
可以忽略并继续。如果断言被违反,可能会导致断轴后果,或者代码可能有一些防御性编程来处理断言违规。
由于 assert 类函数宏被编写为 表达式,它巧妙地避免了“悬空 if
宏”的问题(有一个 if (!(expression)) ...
构造被使用),并且是 表达式 而不是 语句 .