Posted at

あると便利なアサート

More than 5 years have passed since last update.

プログラムのバグをいち早く検知するためにアサートを使うのは当たり前ですが、

assert(p != NULL);

だけ出されてもサッパリですよね……。

例えば、「その p の値を知りたいんだよ!!」って時もあります。

個人的にはアサートで止まった時、以下の情報が欲しくなります。


  • 引っかかったアサートがあるファイル名とその行数。

  • 引っかかった条件

  • 引っかかった値

  • アサートの原因を示すメッセージ

というわけで、せっかくなのでこれらを満たすアサートを作ってしまおうと思って、ちょっと作ってみました。

今回作ったのは、以下のアサートです。

定義名
概要

my_assert(expr,...)
一般的なアサート

my_assert_range(value, min, max)
範囲外の値が指定された

my_assert_array(index, max)
配列外にアクセスした

my_assert_must_not_happen()
本来であれば来ちゃダメなところに来ちゃった

my_assert_null(ptr)
NULL アクセスした

my_assert さえあれば後はどうにでもできますが、

他のアサートも結構頻繁に使うし、使うならメッセージを統一化しておいたほうがわかりやすいので用意しました。

ちなみに、my_assert が条件以外にも可変長引数で引数を指定できるようにしてあるのは、

my_assert_range みたくメッセージに数値を入れたくなる場合があるからです。

以下、コードです。


main.cpp

#include <stdio.h>

#include <stdlib.h>

#ifdef _DEBUG

namespace internal { // 内部実装

// 停止(戻り値が必要なので、独自に定義)
int abort() {
::abort();
return 1;
}

} // namespace internal

// 自作アサート
#define my_assert(expr,...) \
(!(expr) && printf("%s(%d): my_assert failed: "#expr"\n", __FILE__, __LINE__, __VA_ARGS__) && internal::abort())

#else

// 自作アサート(Release 時は無効)
#define my_assert(...)

#endif

// ------------------------------------------------------------------
// いろんな assert
// ------------------------------------------------------------------

// 「値が範囲外だよ!!」
#define my_assert_range(value, min, max) \
my_assert((min <= value) && (value <= max) && "(%d <= [%d] <= %d)", min, value, max)

// 「配列外アクセスだよ!!」
#define my_assert_array(index, max) \
my_assert((0 <= index) && (index < max) && "(0 <= [%d] < %d)", index, max)

// 「ここに来ちゃだめだよ!!」
#define my_assert_must_not_happen() \
my_assert(false && "(must not happen.)")

// 「NULL アクセスだよ!!」
#define my_assert_null(ptr) \
my_assert(ptr != NULL && "(null access.)");

int main()
{
int value = 10;
int index = 3;
int * p = NULL;

my_assert_range(value, 0, 3);
my_assert_array(index, 1);
my_assert_must_not_happen();
my_assert_null(NULL);

return 0;
}



output

main.cpp(50): my_assert failed: (0 <= value) && (value <= 3) && "(0 <= [10] <= 3)"

main.cpp(51): my_assert failed: (0 <= index) && (index < 1) && "(0 <= [3] < 1)"
main.cpp(52): my_assert failed: false && "(must not happen.)"
main.cpp(53): my_assert failed: p != NULL && "(null access.)"