_ASSERT_EXPRについて

[戻る]
2011/05/08(日) 11:09:46
WindowsXP(SP3) VC++2008を使用しております。
c++初心者です。



crtdbg.hに記載してある_ASSERT_EXPRマクロの処理(下部に記載します)についてなので
すが、
exprに対して!を2回記述して、結果を元に戻してる・・・?
一体どういう意図でこのような記述になっているのでしょうか。
また、記載されている英文を読んでみたのですが、英語が苦手なこともあり、
||を使用している意図も良く分かりません。
これは推測なのですが、( a, b )みたいに続けて呼ばせるために||を使用しているので
しょうか。
汎用的に使い回せる用に、このマクロを分解して改造してみたのですが、本家のやり方
が気になってしょうがありません。
よろしければご教授願えませんでしょうか。
よろしくお願いします。


/* Asserts */
/* We use !! below to ensure that any overloaded operators used to evaluate 
expr do not end up at operator || */
#define _ASSERT_EXPR(expr, msg) \
        (void) ((!!(expr)) || \
                (1 != _CrtDbgReportW(_CRT_ASSERT, _CRT_WIDE(__FILE__), 
__LINE__, NULL, msg)) || \
                (_CrtDbgBreak(), 0))

#ifndef _ASSERT
#define _ASSERT(expr)   _ASSERT_EXPR((expr), NULL)
#endif

#ifndef _ASSERTE
#define _ASSERTE(expr)  _ASSERT_EXPR((expr), _CRT_WIDE(#expr))
#endif

tetrapod 2011/05/08(日) 21:01:00
> exprに対して!を2回記述して、結果を元に戻してる・・・?
元に戻るの?本当に?そんなわけない。

!exp は exp が0の時に1、非0の時0 だから
!!exp は exp が0の時0、非0の時1 になる。

んで、なぜこんなまわりくどいことをしているのか・・・だけど、
コメントにあるとおり C++ で演算子がオーバーロードされている場合の対処、かな。
本来 assert() は (C89 では) int に評価される式しか渡してはならないのだが、
故意に(あるいはバグで)クラス変数を直接渡した、としよう。
assert が内部で行っている0かどうかの判断の際に assert の実装次第によって
違う結果が出るのは好ましくない。
ifstream input; // を例にとって見ると
if (input) { ... } は operator void* が使われているし
if (!input) { ... } だと operator ! が使われている。
両者の結果は異なる可能性がある(この例に限れば ios の責任で同じになる)

そのため当該 _ASSERT_EXPR は、内部実装を !! と書くことで
operator void* などのちょっと意外なものが呼び出されるのを防いでいるのだろう。
1回目の (右側の) ! で当該クラスの operator ! を呼び出し
2回目の (左側の) ! は (普通 operator ! は bool/int を返すものだから) 
非クラス型(組み込み型)に対するものが適用させる、
ということに (Microsoft の担当者が) 決めた、わけだ。

επιστημη [E-Mail] [HomePage] 2011/05/08(日) 21:08:20
> ||を使用している意図も良く分かりません。

a || b において、aが真ならbが評価されません。その効果を利用しているのでしょう。

2011/05/09(月) 23:31:30
tetrapod 様
返信ありがとうございます。
分かり易く説明していただきありがとうございます。
理解できました!目から鱗でした。
こういう方法をよく思いつけるなあと感心してしまいます。
またoperator void*や!は例でもあるとおりstreamクラスで使用はしたことはあります
が、実際に実装したこと(見たことも)は無かったので、勉強になりました。
ありがとうございました。


επιστημη 様
返信ありがとうございます。
確かにそうですよね、exprがtrueのときは即座に抜けないといけないですもんね。
1 != _CrtDbgReportWというのは関数が成功したら1を返すけども、そこで抜けさせない
ための処理だったのですね。恥ずかしながら、全然理解出来なかったです…。
ありがとうございました!

2011/05/09(月) 23:32:08
[[解決]]
チェック忘れました。

古いスレッドには追加発言できません。