Finding non-instantiated templates in C++ code

2019-04-24 22:35发布

问题:

What is the best way to find uninstantiated templates in C++ Code?

I have a code base that heavily uses templates. Of course, we want to make sure that the test coverage is high. For all used code, this works very well using gcov.

However, unused templates are reported as non-executable by gcov.

After some googling, it appears that there is no way to force g++ to emit code for these templates (which is only logical, how should the compiler guess any types?) It also appears that there is no way to make gcov recognize the uninstantiated template code as runnable code.

Is there anything "out of the box" that allows me to augment the files generated by the GCC -ftest-coverage -fprofile-arcs instrumentation? By the gcov option documentation of GCC, it would probably be enough to mark the whole template function body as one block since execution will never end up there.

EDIT (background information): I'm working on a header-only template library. My aim here is to find unused/untested functions.

I know that code coverage is flawed but finding uninstantiated code is a very important step towards better tested code. Currently, we are putting checkpoint macros at the beginning of each function. In testing mode, they expand to code that inserts a pair of (file, line) into a global set of passed checkpoints. After running the tests, we manually read all files and compare the reached checkpoints with the set of all available checkpoints.

Finding uninstantiated code is important, for example, because of unintuitive C++ template precedence behaviour, there might be dead code somewhere that the reader or even the author would expect to be used.

回答1:

I think our C++ Test Coverage tool (not GCC-based) does this correctly from your point of view.

It instruments the source code before the compiler sees it; code inside templates get "coverage probes" regardless of whether the template gets used or not. The test coverage display portion of the tool knows where all the probes are; if the template code isn't instantiated, it clearly can't be executed that's what will get reported. You don't have to do any "custom" macro insertion or other, er, BS.

The downside is that if you have a template parameterized by several different types, and template method m1 and m2 are executed for different instantiated types, your coverage for m1 and m2 will be 100% (after all, you executed the instrumented template). It isn't clear this is bad; just that it is how this is interpreted.



回答2:

Okay, since I'm not very versed with GCC, here's a tedious and very time-taking solution, but atleast it works! :)
This test relies on the fact, that some errors in template code aren't detected until actual instantiation, i.e. when dependant names don't actually exist in the template parameter:

template<class T>
struct Example{
  typedef typename T::_123344_non_existent_type instantiation_test;
};

Add such a typedef to every template you have, then compile. Remove it from every struct/class/function the compiler displays an error for and every template that still contains such a typedef when the code finally compiles never gets instantiated. Or you're unlucky and some types do define such a _123344_non_existent_type, though I'd lynch the coworker that is responsible for that. ;)



回答3:

I was having this same problem, so I wrote a program that modifies code coverage reports to show uninstantiated templates. It's not the most elegant, but it works.

There are two phases:

  • Before compilation, you use a C++ program written using Clang's Libtooling to find all of the templates in your code and insert comments before and after them.
  • After you've generated a test coverage report, you run a python script which goes through the report and modifies the lines enclosed by the comments from step 1.

So basically it takes advantage of the fact that even though a lot of your code is left out of the binary it pretty much all ends up in the final coverage report. The macros described in this question were the inspiration for this approach (it's basically an automated version of them that doesn't clutter the source code).

I've only used it on my own code so far, but it should generalize. Issues and pull requests welcome!