このセクションでは、テンプレート関数に対する静的解析と単体テストの実行について説明します。
このセクションの内容:
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 は、クラス中で宣言されている各メンバー関数と静的データ メンバーの #pragma を繰り返すのに相当します。クラス全体をインスタンス生成する場合、#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 の定義がないとヘッダー ファイルは変更されず (タイムスタンプは変わらず)、再コンパイルせずに済むことです。
このようにして、プラグマは通常のコードに影響しません。通常、これはテンプレートを宣言するヘッダー ファイル中で行うべきです。一部のコンパイラでは、テンプレートをヘッダーにのみ置くことができます (テンプレートはソース ファイルを持ちません)。