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:
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:
C++test can perform static analysis and unit testing of instantiated function templates and instantiated members of class templates. In this context, "instantiated" means:
"#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:
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).
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 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."
"#pragma instantiate"
(inline functions, pure virtual functions etc.) has some limitations; see Using "#pragma instantiate" to Enforce Template Instantiation for details.#pragma instantiate
to instantiate the constructor/destructor, or create an instance of a given class in the tested code (as a global variable etc.).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.
This section explains how to enable and create stubs for C++ templates and lists the usage limitations of that functionality.
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.). |
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.
Constructors and destructors
Conversion operators
Implicitly defined functions
Defaulted and deleted functions
Friend functions
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.