このセクションでは、テンプレート関数に対する静的解析と単体テストの実行および C++ テンプレートのスタブを使用する方法について説明します。
このセクションの内容
C++ テンプレートからのコード カバレッジの収集
C++test は、C++ テンプレートからコード カバレッジ情報を収集することができます。コード カバレッジ情報は、すべてのテンプレート インスタンスについて収集され、累積的にレポートされます。C++ テンプレートからコード カバレッジを収集するには、以下の設定を行います。
- テスト コンフィギュレーションを開き、[実行] > [全般] タブに移動します。
- [実行の詳細] で、[インストゥルメント モード] の [編集] ボタンをクリックします。
- テスト コンフィギュレーションの [実行] > [インストゥルメント機能] パネルで [詳細オプション] の [C++ テンプレートからのコード カバレッジを有効化] チェックボックスをオンにします。
C++test でのインスタンスのサポート
C++test は、インスタンス化された関数テンプレートとインスタンス化されたクラス テンプレートのメンバーに対して静的解析と単体テストを実行できます。ここで、「インスタンス化された」とは次のことを意味します。
- テスト対象ソースでインスタンス化された (使用された)。
- テスト対象ソースで"
#pragma instantiate
" を使ってインスタンス化された (詳細については「"#pragma instantiate" によるテンプレートのインスタンス生成」 を参照)。今後の説明では、そのような関数を 「インスタンス
」と呼びます。
C++test は:
- インスタンスの本体の静的解析を実行できます。
- インスタンスのためのテスト ケースを自動生成できます。
インスタンスの本体のインストゥルメントには制限があります。インスタンスからのスタブ化された呼び出しはありません。また、例外のレポートにおいてスタック トレースが不正確な場合があります (これらの制限は明示的なテンプレートの特殊化にはあてはまりません)。
"#pragma instantiate" によるテンプレートのインスタンス生成
あるテンプレート インスタンスがテスト対象ソースで使用されていない場合、テスト対象コードに #pragma instantiate
を挿入して強制的にテンプレートをインスタンス生成できます。
#pragma instantiate atype<int>
Edison Design Group, Inc. の『C++ Front End Internal Documentation』には次のように説明があります。
#pragma instantiate は引数として次のものを取ります。
- テンプレート クラスの名前 A<int>
- テンプレート クラスの宣言 class A<int>
- メンバー関数の名前 A<int>::f
- 静的データ メンバーの名前 A<int>::i
- 静的データの宣言 int A<int>::i
- メンバー関数の宣言 void A<int>::f(int, char)
- テンプレート関数の宣言 char* f(int, float)
引数がテンプレート クラス名であるプラグマ (例: A<int> または class A<int>) は、クラスで宣言されている関数および静的データ メンバーそれぞれにプラグマを繰り返しているのと同等です。クラス全体をインスタンス生成する場合、#pragma do_not_instantiate
を使って、特定のメンバー関数または静的データ メンバーをインスタンス生成から除外できます。例:
#pragma instantiate A<int>
#pragma do_not_instantiate A<int>::f
インスタンスを生成するには、テンプレート エンティティのテンプレート定義がコンパイル時に存在しなければなりません。#pragma instantiate を使って明示的にインスタンス生成を要求していて、「テンプレート定義がない」または「具体的な定義がある」場合、エラーが発生します。
template <class T> void f1(T); // No body provided template <class T> void g1(T); // No body provided void f1(int) {} // Specific definition void main() { int i; double d; f1(i); f1(d); g1(i); g1(d); } #pragma instantiate void f1(int) // error - specific definition #pragma instantiate void g1(int) // error - no body provided
f1(double) および g1(double) は、本体がないためインスタンス生成されません。しかし、コンパイル時にエラーは発生しません。( リンク時に本体がない場合はリンカー エラーが発生します。)
メンバー関数名 (たとえば A<int>::f ) を引数として使用できるのは、この関数名が 1 つのユーザー定義メンバー関数 (つまりオーバーロードされた関数ではない関数) を参照している場合だけです。コンパイラ生成の関数は対象外です。そのため、たとえ同じ名前のコンパイラ生成コピー コンストラクターが存在する場合でも、引数名はユーザー定義コンストラクターを参照できます。オーバーロードされたメンバー関数をインスタンス生成するには、完全なメンバー関数宣言を #pragma instantiate char* A<int>::f(int, char*)
として渡します。
#pragma instantiate の引数として、コンパイラ生成関数、インライン関数、および純粋仮想関数を渡すことはできません。
使用の制限事項
#pragma instantiate
には、インライン関数や純粋仮想関数などについての制限事項があります。詳細については「"#pragma instantiate" によるテンプレートのインスタンス生成」 を参照してください。- C++test は、コンストラクター/デストラクターがインスタンス生成されないテンプレート クラスのためのテスト ケースを自動生成できません。代替方法として、
#pragma instantiate
を使ってコンストラクター/デストラクターをインスタンス生成するか、テスト対象コード中でグローバル変数などとして該当クラスのインスタンスを作成してください。
ヒント
テンプレートのインスタンス生成を行うソース コードがない場合、次の方法があります。
#ifdef PARASOFT_CPPTEST #pragma instantiate ... ... #endif
または、次の方法もあります。
#ifdef PARASOFT_CPPTEST #include "pragma_instantiate.h" #endif
ここで、インクルードされるヘッダー ファイルは、必要な型によるインスタンス生成を持ちます。
2 番目の方法の利点は、型セットに追加した場合に、PARASOFT_CPPTEST の定義がないとヘッダー ファイルは変更されず (タイムスタンプは変わらず)、再コンパイルせずに済むことです。
このようにして、プラグマは通常のコードに影響しません。通常、これはテンプレートを宣言するヘッダー ファイル中で行うべきです。一部のコンパイラでは、テンプレートをヘッダーにのみ置くことができます (テンプレートはソース ファイルを持ちません)。
C++ テンプレートに対してスタブを使用する
このセクションでは、C++ のテンプレートに対してスタブを有効化し、作成する方法を説明するとともに、この機能の使用上の制限事項を挙げます。
C++ テンプレートに対してスタブを有効化する
- テスト コンフィギュレーションを開き、[実行] > [全般] タブに移動します。
- [実行の詳細] で、[インストゥルメント モード] の [編集] ボタンをクリックします。
- [スタブ] セクションで [C++ テンプレートのスタブを有効化] オプションをオンにします。
一貫性を保証するため、プロジェクトで使用する 単体テスト関連のテスト コンフィギュレーションでは、必ず [C++ テンプレートに対してスタブを有効化する] オプションをオンにします (Run Unit Tests、Generate Stubs など)。
テンプレートに対してスタブを作成する
C++ テンプレートのスタブを有効化してテスト コンフィギュレーションを実行した後、スタブを定義できる C++ テンプレート関数がスタブ ビューに表示されます。
C++ テンプレート関数に対してスタブを作成するには、関数を右クリックして [ユーザー スタブの作成] を選択します。
使用の制限事項
- C++ テンプレートのスタブは以下のコンパイラでサポートされています。
- GNU GCC 5.x 以降
- Clang C/C++ Compiler 5 以降
- Microsoft Visual C++ 14.0 (2015) 以降
- GNU GCC 5.x 以降
- C++ テンプレートのスタブは以下をサポートしません。
コンストラクターおよびデストラクター
変換演算子
暗黙的に定義された関数
default および delete 宣言された関数
friend 関数
- C++ テンプレートのスタブを使用するには、すべてのテンプレート引数の型が元のテンプレート定義より前に (インスタンスが作成されるすべての翻訳単位で) 定義されている必要があります。
テンプレートに対してスタブを使用できないコードの例:
// = from func.h // "S" type (used in instance below) is unknown template <typename T> void func(T t) {} // = from struct.h struct S {}; // stub for func<S>(S) cannot be used void m(S s) { func(s); // call to be stubbed }
テンプレートのスタブを有効にするには、コードを次のように変更します。
// = from struct.h // Type S defined before template function definition struct S {}; // = from func.h // "S" type (used in instance below) is defined template <typename T> void func(T t) {} // stub for func<S>(S) can be used void m(S s) { func(s); // call to be stubbed }
状況によっては、インクルード ファイルの順序、構造、内容を変更する必要があります。