本主题说明 C++test 如何对模板函数进行编码标准分析和单元测试,以及如何为 C++ 模板使用桩函数。

章节目录:

从 C++ 模板收集代码覆盖率

C++test 可以收集 C++ 模板的代码覆盖率信息。为所有模板实例收集和报告代码覆盖率信息。启用从 C++ 模板收集代码覆盖率的步骤:

  1. 打开测试配置,前往执行> 常规选项卡。
  2. 执行细节下方,点击插桩模式部分的编辑
  3. 在测试配置的执行选项卡“插桩功能”面板的高级选项部分,勾选启用 C++ 模板的覆盖率选项。

另请参阅在 C++ 模板中推算布尔表达式

了解 C++test 的实例支持

C++test 可以对实例化的函数模板和实例化的类模板成员执行静态分析和单元测试。在这种情况下,“实例化”是指:

C++test 可以:

  • 对实例主体执行静态分析。
  • 自动为实例生成测试用例。

实例的主体插桩有一定限制。因此,没有来自实例的桩函数调用,并且在报告异常时可能存在不准确的堆栈追(这些限制不适用于显式模板特化)。

使用“#pragma instantiate”来强制模板实例化

如果在测试的源代码中没有使用给定的模板实例,您可以通过在测试的代码中插入 #pragma instantiate 来强制模板实例化,例如:

#pragma instantiate atype<int>

C++ Front End Internal Documentation 中,Edison Design Group, Inc. 解释称:

“实例化 pragma 的参数可以是:

  • 模板类名称 A<int>
  • 模板类声明类 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。当实例化整个类时,可以使用 do_not_instantiate 编译指示排除给定的成员函数或静态数据成员。例如:

#pragma instantiate A<int>
#pragma do_not_instantiate A<int>::f

要进行实例化,模板实体的模板定义必须出现在编译中。如果使用实例化 pragma 显式请求实例化并且没有可用的模板定义或未提供特定定义,则会报告错误。


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)仅在引用单个用户定义的成员函数(即,不是重载函数)时才可用作 pragma 参数。不考虑编译器生成的函数,因此即使存在由编译器生成的同名复制构造函数,名称也可以引用用户定义的构造函数。可以通过提供完整的成员函数声明来实例化重载的成员函数,如 #pragma instantiate char* A<int>::f(int, char*)

实例化 pragma 的参数不能是编译器生成的函数、内联函数或纯虚函数。”

使用限制

  • "#pragma instantiate"(内联函数、纯虚函数等)有一定的局限性;详细信息请参阅使用“#pragma instantiate”来强制模板实例化
  • C++test 不能为没有实例化构造函数/析构函数的模板类自动生成测试用例;相反,可以使用 #pragma instantiate 来实例化构造函数、析构函数,或者在测试的代码中创建给定类的实例(作为全局变量等)。

提示

如果您没有任何可进行模板实例化的源代码,则可以使用


#ifdef PARASOFT_CPPTEST 
#pragma instantiate ... 
...
#endif

或者,您可以使用

#ifdef PARASOFT_CPPTEST
#include "pragma_instantiate.h" 
#endif

其中包含的头文件包含具有必要类型的实例化。

第二个选项的优点在于,如果向类型集添加内容,而没有定义 PARASOFT CPPTEST(没有时间戳更改),头文件不会发生更改,从而节省重新编译的时间。

这样,pragma 就不会干扰您的常规代码。通常,这应该在声明模板的头文件中完成。对于某些编译器,模板只能位于头文件中——它们没有源文件。

为 C++ 模板使用桩函数

本节说明如何为 C++ 模板启用和创建桩函数,并列出该功能的使用限制。

为 C++ 模板启用桩函数

  1. 打开测试配置,前往执行> 常规选项卡。
  2. 执行细节下方,点击插桩模式部分的编辑
  3. 选择打桩部分的启用 C++ 模板的桩选项。

为保持一致性,应确保在项目中使用的所有“Unit Testing”相关测试配置中(例如,Run Unit Tests、Generate Stubs 等)都选择启用 C++ 模板的桩选项。

为模板创建桩函数

运行启用了 C++模板桩函数的测试配置后,桩函数视图将显示可以为其定义桩函数的 C++ 模板函数:

要为 C++ 模板函数创建桩函数,可右键点击该函数并选择创建用户桩函数

使用限制

  • 以下编译器支持 C++ 模板的桩函数:
    • GNU GCC 5.x 或更高版本
    • Clang C/C++ 编译器 5 或更高版本
    • Microsoft Visual C++ 14.0 (2015) 或更高版本
  • 以下内容不支持 C++ 模板的桩函数:
    • 构造函数和析构函数

    • 转换运算符

    • 隐式定义函数

    • 默认和删除的函数

    • 友元函数

  • 要使用 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
}

根据情况,您可能需要更改包含文件的顺序、结构或内容。


  • No labels