ホームに戻る
出典 :
関連 :
目次 :
アサーション(Assertion)とは
プログラム内のある時点(またはコンパイル時点)で想定される状況(条件)を記述し、その状況に無かった場合にエラーを発する機能。
「その時点においては当然そうである(そうでない場合は利用者のミスである)」ことを表明することで、引数などの誤りをデバッグ段階で検出しやすくなる。
assert()
実行時に特定の条件を満たすことを想定する。
条件を満たさなかった場合は abort() (C) / std::abort() (C++)を呼び出してプログラムを異常終了させる。
使用するには <assert.h> (C) / <cassert> (C++)のインクルードが必要。尚、assert() は関数形式マクロである。
実装例(main.cpp) :
#include <cassert> //< アサーション用
#include <iostream>
#include <limits>
#include <vector>
// ベクトルの値の中で最大のものを返す
int Max(const std::vector<int>& v)
{
// アサーション
// v が空でないことを強制する
// ( v が空の場合は異常終了)
assert( !v.empty() );
int max = std::numeric_limits<int>::min();
for( auto e : v )
{
if( e > max )
{
max = e;
}
}
return max;
}
// main()
int main()
{
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2;
std::cout << Max(v1) << std::endl;
std::cout << Max(v2) << std::endl;
return 0;
}
実行結果 :
$ ./a.exe
5
assertion "!v.empty()" failed: file "main.cpp", line 11, function: int Max(const std::vector<int>&)
Aborted (コアダンプ)
上記のコードでは assert() を使用している。
assert() の引数は「その時点で true となっているはずのブール式」であり、上記では引数 v (ベクトル)が空でないことを想定している。
main() 関数ではアサーションを組み込んだ Max() を呼び出しているが、空のベクトル v2 を渡すとアサーション違反となり、プログラムが異常終了する。
その際、異常個所のファイル名、行番号、関数名、アサーション内容が出力されるため、どのアサーションに違反したのかを容易に追跡できる。
(但し、出力内容は処理系に依存する。)
このように、関数の引数に制約を設ける場合などに有用である。
注意が必要な点
assert() はあくまで「開発中のバグを取り除くこと」を想定した機能であり、(通常)リリースビルド( NDEBUG 定義)では無効となる。
前節のコードをリリースビルドした場合の実行結果は下記のようになり、アサーションが機能していないことがわかる。
$ ./a.exe
5
-2147483648
if 文との比較
if 文の場合
bool f(int v)
{
if( v < 0 )
{
std::cerr << "v must be a positive number.\n";
return false;
}
//...
return true;
}
assert() の場合
void f(int v)
{
assert(v >= 0);
//...
}
if 文を用いたエラー処理(ガード節)よりも、assert() を用いるほうが記述が簡潔で、制約が課されていることが視覚的にわかりやすい。
また、リリースビルド後に制約違反が起こらないことが保証されている場合、if 文は比較に伴うコストが発生するのに対し、
assert() は文自体が無効化されるため、パフォーマンス面でも有利である。
当然、公開APIなどリリース後も引数チェックが必要となるケースは存在するため、すべてをアサーションで代替することはできない。
static_assert()
コンパイル時に特定の条件を満たすことを想定する。想定する条件が満たされない場合はコンパイルエラーとなる。
static_assert() はマクロではなく、言語文法の一部として実装されている。assert() と異なり、標準ライブラリのインクルードは不要である。
// long 型のサイズが 8 バイトであることを想定する
// (それ以外のサイズだとコンパイルエラーとなる)
static_assert(sizeof(long) == 8, "long type is not 8 bytes.");
int main()
{
}
assert() と同様、括弧内には true が想定されるブール式を記述する。
(失敗時のメッセージ(文字列リテラル)を併せて指定できるが、C++17ではメッセージを省略可能。)
判定はコンパイル時であるため、条件式は定数式(コンパイル時定数)でなければならない。