CV_Assert, CV_DbgAssert, CV_StaticAssert

この記事は[OpenCV Advent Calendar 2015](http://qiita.com/advent-calendar/2015/opencv)の5日目の記事です．

また，前回の[OpenCVのエラーコード，エラーメッセージ表示機能，例外処理機能](http://qiita.com/dandelion1124/items/2f92eb1ecca204ba33ed)の内容と
関連があるので事前に一読頂くと理解が深まるかもしれません~~（ステマ）~~．

# はじめに
OpenCVにはAssert関連の関数がいくつか提供されています．
この記事では，OpenCVが提供するAssert関数とそれらの使い方を紹介します．

# Assertとは
Assertはプログラム内の矛盾を検出するためのデバッグ機能です．
また，Assertを使うことで「この条件を満たすべき」という実装者の意図を
コード上に示すことができます．

このAssert機能をうまく活用することで不具合が発生した場合にプログラムが
どの時点で意図しない状態になっているかを見付けるきっかけにすることができます．

# OpenCVが提供するAssert関連マクロ
OpenCVが提供するAssert関連のマクロは以下の3つです．

| マクロ名 | マクロの振る舞い | チェックのタイミング |
|:--|:--|:--|
| CV_Assert | 条件を満たさない場合はプログラムを終了<br>（Release，Debug） | 実行時（ランタイム） |
| CV_DbgAssert | 条件を満たさない場合はプログラムを終了<br>（Debug時のみ） | 実行時（ランタイム）|
| CV_StaticAssert | 条件を満たさない場合はコンパイルエラー | コンパイル時 |

一般的によく知られている[assert関数](https://msdn.microsoft.com/ja-jp/library/9sb57dw4.aspx)（\<assert.h\>や\<cassert\>で定義）に処理が近いため，
CV_DbgAssertマクロの方がイメージが掴みやすいかもしれません．

# CV_Assert
CV_AssertマクロはRelease，Debug問わず，実行時に条件式を満たさない場合は
エラーメッセージを出力してプログラムを終了します．

CV_Assertマクロは<code>core/include/opencv2/core/base.hpp</code>で定義されています．
マクロの実装は以下の通りです．

<code>#define CV_Assert( expr ) if(!!(expr)) ; else cv::error( cv::Error::StsAssert, #expr, CV_Func, __FILE__, __LINE__ )</code>

CV_Assertマクロで指定した条件式を満たさない場合はエラーコード（<code>cv::Error::StsAssert</code>）を
指定して<code>cv::error</code>関数をコールします．また，<code>cv::error</code>関数内では，

1. エラーメッセージを表示する
1. 例外を投げる

という処理を行った後，プログラムを終了させます．

# CV_DbgAssert
CV_DbgAssertマクロはDebug時のみ，実行時に条件式を満たさない場合は
エラーメッセージを出力してプログラムを終了します．

CV_DbgAssertマクロは<code>core/include/opencv2/core/base.hpp</code>で定義されています．
マクロの実装は以下の通りです．

```cpp
#ifdef _DEBUG
#  define CV_DbgAssert(expr) CV_Assert(expr)
#else
#  define CV_DbgAssert(expr)
#endif
```

上記コードを見てわかるように<code>_DEBUG</code>が定義されているときはCV_Assertマクロを実行し，
そうでない場合は何もしないという非常にシンプルな実装です．

# CV_StaticAssert
CV_StaticAssertマクロは前述のマクロとは少し毛色が違い，実行時ではなく
コンパイル時にチェックを行い，条件式が満たされない場合はコンパイルエラーとして出力されます．

CV\_StaticAssertマクロは<code>core/include/opencv2/core/base.hpp</code>で定義されています．
マクロの実装は以下の通りです（ここではVisual Studioの場合のみ抜粋）．


```cpp
#elif defined(_MSC_VER)
#  if _MSC_VER >= 1600 /* MSVC 10 */
#    define CV_StaticAssert(condition, reason)    static_assert((condition), reason " " #condition)
#  endif
#endif
```

## CV_StaticAssert使用時の注意点
前述のCV\_StaticAssertマクロ実装を読むとわかるように<code>if _MSC_VER >= 1600 </code>とあるため，
Visual Studioの場合，Visual Studio 2010以降でないとCV\_StaticAssertマクロが機能しない点に注意が必要です．

# サンプルコード
## サンプルコード（CV_Assert）
CV_Assertマクロを使って画像の範囲外をアクセスしていないかをチェックするサンプルコードです．
この例では画像の範囲外の座標を指定しており，Release，Debug問わず
実行時にアサーションエラーが発生して終了します．

```cpp:assert_test.cpp
#include <opencv2/core.hpp>
#include <iostream>

void testFunc(cv::InputArray src_, int x, int y)
{
    cv::Mat src = src_.getMat();

    // 範囲外をアクセスしていないかの検証
    CV_Assert((x >= 0) && (x < src.cols));
    CV_Assert((y >= 0) && (y < src.rows));

    cv::Vec3b val = src.at<cv::Vec3b>(y, x);
    std::cout << "R:" << (unsigned int)val[2] << std::endl;
    std::cout << "G:" << (unsigned int)val[1] << std::endl;
    std::cout << "B:" << (unsigned int)val[0] << std::endl;
}

int main(int argc, const char* argv[])
{
    // 幅512px、高さ512px、3チャンネルのインスタンスを生成する
    cv::Mat img(cv::Size(512, 512), CV_8UC3, cv::Scalar(0, 0, 255));

    // testFunc関数を実行する
    testFunc(img, 0, 512);

    return 0;
}
```

## サンプルコード（CV_DbgAssert）
CV_DbgAssertマクロを使って画像の範囲外をアクセスしていないかをチェックするサンプルコードです．
この例では画像の範囲外の座標を指定しており，Debug時のみ実行時にアサーションエラーが発生して終了します．

```cpp:dbg_assert_test.cpp
#include <opencv2/core.hpp>
#include <iostream>

void testFunc(cv::InputArray src_, int x, int y)
{
    cv::Mat src = src_.getMat();

    // 範囲外をアクセスしていないかの検証
    CV_DbgAssert((x >= 0) && (x < src.cols));
    CV_DbgAssert((y >= 0) && (y < src.rows));

    cv::Vec3b val = src.at<cv::Vec3b>(y, x);
    std::cout << "R:" << (unsigned int)val[2] << std::endl;
    std::cout << "G:" << (unsigned int)val[1] << std::endl;
    std::cout << "B:" << (unsigned int)val[0] << std::endl;
}

int main(int argc, const char* argv[])
{
    // 幅512px、高さ512px、3チャンネルのインスタンスを生成する
    cv::Mat img(cv::Size(512, 512), CV_8UC3, cv::Scalar(0, 0, 255));

    // testFunc関数を実行する
    testFunc(img, 0, 512);

    return 0;
}
```

## サンプルコード（CV_StaticAssert）
CV_StaticAssertマクロを使ってコンパイル時に条件式を満たすかチェックするサンプルコードです．
この例ではテンプレート引数の型が数値であるかをコンパイル時にチェックしています．

```cpp:static_assert_test.cpp
#include <opencv2/core.hpp>
#include <iostream>

template <typename T>
struct Test
{
    // テンプレート引数の型が数値型であるかチェック
    CV_StaticAssert
    (
        std::_Is_numeric<T>::value,
        "T is a numeric type."
    );
    T value;
};

int main(int argc, const char* argv[])
{
    Test<int>         test1; // OK
    Test<float>       test2; // OK
    Test<int *>       test3; // NG
    Test<std::string> test4; // NG
}
```

<code>int *</code>と<code>std::string</code>は数値型でないのでコンパイル時にエラーとなり，
開発者は実装ミスにすぐ気付くことができます．

# おわりに
今回，OpenCVが提供するAssertマクロ（CV_Assert，CV_DbgAssert，CV_StaticAssert）を紹介しました．
これらのマクロをうまく活用することで，不具合が起きた際の原因特定に役立てましょう！

# 備考
筆者は以下の環境で動作確認しました．

* [OpenCV 3.0.0](https://github.com/Itseez/opencv/releases/tag/3.0.0)
* Windows 8.1 Pro（64bit）
* Visual Studio 2013 Update5