This topic explains how C++test performs coding standards analysis and unit testing on template functions, and how to use stubs for C++ templates.
In this section:
Collecting Code Coverage from C++ Templates
C++test can collect code coverage information for C++ templates. Code coverage information is collected and reported cumulatively for all template instances. To enable collecting code coverage from C++ templates:
- Open the test configuration and go to the Execution> General tab.
- In Execution details, click Edit in the Instrumentation mode section.
- Enable the Enable coverage for C++ templates option in the Advanced options section of the Test's Configuration's Execution tab "Instrumentation features" panel.
See also Computing Boolean Expressions in C++ Templates.
Understanding C++test’s Instance Support
C++test can perform static analysis and unit testing of instantiated function templates and instantiated members of class templates. In this context, "instantiated" means:
- Instantiated (used) in the tested source.
- Instantiated with
"#pragma instantiate"
in the tested source; see Using "#pragma instantiate" to Enforce Template Instantiation for details. Such functions will be further addressed as"instances".
C++test can:
- Perform static analysis of instances' bodies.
- Automatically generate test cases for instances.
The instances' bodies instrumentation is limited. As a result, there are no stubbed calls from instances, and possibly inaccurate stack traces when reporting exceptions (these restrictions do not apply to explicit template specializations).
Using "#pragma instantiate" to Enforce Template Instantiation
If a given template instance is not used in the tested source, you can enforce template instantiation by inserting #pragma instantiate
into the tested code; for example:
#pragma instantiate atype<int>
In C++ Front End Internal Documentation, the Edison Design Group, Inc. explains:
"The argument to the instantiation pragma may be:
- a template class name A<int>
- a template class declaration class A<int>
- a member function name A<int>::f
- a static data member name A<int>::i
- a static data declaration int A<int>::i
- a member function declaration void A<int>::f(int, char)
- a template function declaration char* f(int, float)
A pragma in which the argument is a template class name (e.g., A<int> or class A<int>) is equivalent to repeating the pragma for each member function and static data member declared in the class. When instantiating an entire class a given member function or static data member may be excluded using the do_not_instantiate
pragma. For example:
#pragma instantiate A<int>
#pragma do_not_instantiate A<int>::f
The template definition of a template entity must be present in the compilation for an instantiation to occur. If an instantiation is explicitly requested by use of the instantiate pragma and no template definition is available or a specific definition is provided, an error is issued.
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) and g1(double) will not be instantiated (because no bodies were supplied) but no errors will be produced during the compilation (if no bodies are supplied at link time, a linker error will be produced).
A member function name (e.g., A<int>::f) can only be used as a pragma argument if it refers to a single user defined member function (i.e., not an overloaded function). Compiler-generated functions are not considered, so a name may refer to a user defined constructor even if a compiler-generated copy constructor of the same name exists. Overloaded member functions can be instantiated by providing the complete member function declaration, as in #pragma instantiate char* A<int>::f(int, char*)
The argument to an instantiation pragma may not be a compiler-generated function, an inline function, or a pure virtual function."
Usage Limitations
"#pragma instantiate"
(inline functions, pure virtual functions etc.) has some limitations; see Using "#pragma instantiate" to Enforce Template Instantiation for details.- C++test cannot automatically-generate test cases for template classes for which a constructor/destructor is not instantiated; instead, either use
#pragma instantiate
to instantiate the constructor/destructor, or create an instance of a given class in the tested code (as a global variable etc.).
Tips
If you do not have any source code that does template instantiations, you could use
#ifdef PARASOFT_CPPTEST #pragma instantiate ... ... #endif
Alternatively, you could use
#ifdef PARASOFT_CPPTEST #include "pragma_instantiate.h" #endif
where the included header contains the instantiations with necessary types.
The benefit of the second option is that if you add to the type set, your headers do not get changed without PARASOFT_CPPTEST defined (no timestamp change) and you save on recompilation.
This way, the pragma does not interfere with your regular code. Typically, this should be done in header files that declare templates. With some compilers, templates can only be in headers—they do not have source files.
Using Stubs for C++ Templates
This section explains how to enable and create stubs for C++ templates and lists the usage limitations of that functionality.
Enabling Stubs for C++ Templates
- Open the test configuration and go to the Execution> General tab.
- In Execution details, click Edit in the Instrumentation mode section.
- Select the Enable stubs for C++ templates option in the Stubs section.
For consistency, be sure to select the Enable stubs for C++ templates option in all "Unit Testing" related test configurations used in your project (e.g. Run Unit Tests, Generate Stubs, etc.).
Creating Stubs for Templates
After running a test configuration with stubs for C++ templates enabled, the Stubs view will show C++ template functions for which stubs can be defined:
To create a stub for a C++ template function, right-click the function and choose Create User Stub.
Usage Limitations
- Stubs for C++ templates are supported for the following compilers:
- GNU GCC 5.x or newer
- Clang C/C++ Compiler 5 or newer
- Microsoft Visual C++ 14.1 (2017) or newer
- GNU GCC 5.x or newer
- Stubs for C++ templates are not supported for:
Constructors and destructors
Conversion operators
Implicitly defined functions
Defaulted and deleted functions
Friend functions
- To use stubs for C++ templates, all template argument types must be defined before the original template definition (in all translation units where the instance is created).
Example of code where stubs for templates cannot be used:
// = 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 }
To enable stubs for templates, the code can be changed to:
// = 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 }
Depending on the situation you might need to change the order, structure or content of the include files.