私は有益な記事を偶然見つけました: http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ これは、私の中に存在する多くの問題を指摘しました現在のデバッグマクロスイート。
リンクをクリックすると、記事の最後近くにマクロの最終バージョンの完全なコードが表示されます。
提示された一般的な形式は次のとおりです(転置が間違っている場合は誰かが私を修正してください):
_#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)
_
私が学んだことでコードを変更することを考えているときに、その記事のコメントに投稿されたいくつかの興味深いバリエーションに気づきました。
1つは、このマクロを三項演算子(つまり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
_
この場合、論理および_&&
_の使用は特にスマートであると思いました。このバージョンは、if
または3項_?:
_を使用するバージョンよりも柔軟性があるようです。さらに良いのは、_assert_handler
_の戻り値を使用して、プログラムを停止する必要があるかどうかを判断できることです。ただHALT()
ではなく_(HALT(), 1)
_である理由はわかりませんが。
ここで私が見落としている2番目のバージョンの特定の欠点はありますか?マクロを囲むdo{ } while(0)
は不要ですが、if
sを処理する必要がないため、ここでは不要であるようです。
どう思いますか?
CおよびC++標準ライブラリでは、assert
は関数として機能するために必要なマクロです。その要件の一部は、ユーザーがexpressionsで使用できる必要があることです。たとえば、標準のassert
を使用すると、
_int sum = (assert(a > 0), a) + (assert(b < 0), b);
_
機能的には同じです
_assert(a > 0 && b < 0)
int sum = a + b;
_
前者は式を書くための非常に良い方法ではないかもしれませんが、トリックは多くのより適切なケースで依然として非常に役立ちます。
つまり、独自のカスタムASSERT
マクロが標準のassert
の動作と使いやすさを模倣したい場合、if
またはdo { } while (0)
をASSERT
は問題外です。その場合、式は_?:
_演算子または短絡論理演算子のいずれかを使用することを意味します。
もちろん、標準のようなカスタムASSERT
を作成する必要がない場合は、if
を含め、何でも使用できます。リンクされた記事はこの問題を考慮していないようですが、これはかなり奇妙です。私の意見では、関数のようなアサートマクロは、関数のようなアサートマクロよりも明らかに便利です。
_(HALT(), 1)
_演算子は有効な引数を必要とするため、_&&
_...と同様に行われます。 HALT()
の戻り値は、_&&
_の有効な引数を表していない可能性があります。私が知っていることはvoid
になる可能性があります。つまり、単なるHALT()
は、単に_&&
_の引数としてコンパイルされません。 _(HALT(), 1)
_は常に_1
_に評価され、タイプint
を持ちます。これは常に_&&
_の有効な引数です。したがって、HALT()
のタイプに関係なく、_(HALT(), 1)
_は常に_&&
_の有効な引数です。
do{ } while(0)
に関する最後のコメントはあまり意味がありません。マクロをdo{ } while(0)
で囲む目的は、マクロ定義内のif
sではなく、外部if
sを処理することです。 alwaysマクロは外部if
で使用される可能性が常にあるため、外部if
sを処理する必要があります。後者の定義では、そのマクロは式であるため、do{ } while(0)
は必要ありません。そして、式であるため、外部のif
sには既に問題はありません。したがって、それらについて何もする必要はありません。さらに、上で述べたように、それをdo{ } while(0)
で囲むと、その目的が完全に無効になり、非式になります。
完全を期すために、C++でのドロップイン2ファイルアサートマクロ実装を公開しました。
_#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;
}
_
プロンプトが表示されます:
_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:
_
どこ
abort()
(Windowsでは、システムはユーザーにデバッガーをアタッチするように要求します)abort()
を呼び出しますあなたはそこでそれについてもっと知ることができます:
お役に立てば幸いです。
HALT()
ではなく(HALT(), 1)
である理由はわかりませんが。
HALT
はexit
のマクロ(または他の代替名)かもしれません。 HALT
コマンドにexit(1)
を使用したいとします。 exit
はvoid
を返します。これは、&&
の2番目の引数として評価できません。最初の引数を評価し、次に2番目の引数の値を評価して返すコンマ演算子を使用すると、HALT()
のためにそのポイントに到達しなかったとしても、整数(1)で&&
に戻ります。それまでずっと停止します。
基本的に、HALT
に値を入力する関数は、おそらくvoid
の戻り値を持ちます。値を返すのは意味がないからです。我々はcouldマクロのためだけにint
を返すようにしますが、すでにマクロでハッキングしている場合、もう少しハッカーが害を及ぼすことはありませんが、 ?