Поиск неинстанцированных шаблонов в коде C++
Каков наилучший способ найти необоснованные шаблоны в коде C++?
У меня есть кодовая база, которая интенсивно использует шаблоны. Конечно, мы хотим убедиться, что тестовое покрытие является высоким. Для всего используемого кода это работает очень хорошо, используя gcov
,
Тем не менее, неиспользуемые шаблоны сообщаются как неисполняемые gcov
,
После некоторого поиска в Google, кажется, что нет никакого способа заставить g++
испускать код для этих шаблонов (что логично, как компилятор должен угадывать какие-либо типы?) Также представляется, что нет способа заставить gcov распознавать необоснованный код шаблона как исполняемый код.
Есть ли что-нибудь "из коробки", что позволяет мне увеличивать файлы, сгенерированные GCC -ftest-coverage -fprofile-arcs
измерительные приборы? С помощью документации по опциям gcov в GCC, вероятно, будет достаточно отметить все тело функции шаблона как один блок, так как выполнение никогда не закончится там.
РЕДАКТИРОВАТЬ (справочная информация): я работаю над библиотекой шаблонов только для заголовка. Моя цель здесь - найти неиспользуемые / непроверенные функции.
Я знаю, что покрытие кода имеет недостатки, но поиск неосуществленного кода является очень важным шагом на пути к лучшему тестированию кода. В настоящее время мы помещаем макросы контрольных точек в начале каждой функции. В режиме тестирования они расширяются до кода, который вставляет пару (file, line)
в глобальный набор пройденных контрольных точек. После запуска тестов мы вручную читаем все файлы и сравниваем достигнутые контрольные точки с набором всех доступных контрольных точек.
Поиск неосуществленного кода важен, например, из-за неинтуитивного поведения приоритета шаблона C++, где-то может быть мертвый код, который читатель или даже автор ожидают использовать.
3 ответа
Я думаю, что наш C++ Test Coverage (не на основе GCC) делает это правильно с вашей точки зрения.
Он обрабатывает исходный код до того, как его увидит компилятор; код внутри шаблонов получает "пробники покрытия" независимо от того, используется шаблон или нет. Часть отображения тестового покрытия инструмента знает, где находятся все зонды; если код шаблона не создан, он явно не может быть выполнен, о чем будет сообщено. Вам не нужно делать какую-либо "пользовательскую" вставку макроса или что-то еще, например, BS.
Недостатком является то, что если у вас есть шаблон, параметризованный несколькими различными типами, и метод шаблона m1 и m2 выполняются для различных экземпляров типов, ваше покрытие для m1 и m2 будет 100% (в конце концов, вы выполнили инструментальный шаблон). Не ясно, что это плохо; просто это то, как это интерпретируется.
У меня возникла такая же проблема, поэтому я написал программу, которая модифицирует отчеты о покрытии кода, чтобы показывать необоснованные шаблоны. Это не самый элегантный, но работает.
Есть две фазы:
- Перед компиляцией вы используете программу на C++, написанную с помощью Libtooling Clang, чтобы найти все шаблоны в вашем коде и вставить комментарии до и после них.
- После того, как вы сгенерировали отчет о тестовом покрытии, вы запускаете скрипт python, который просматривает отчет и модифицирует строки, заключенные в комментарии из шага 1.
Таким образом, в основном используется тот факт, что, хотя большая часть вашего кода не включена в двоичный код, в конечном итоге все это заканчивается в окончательном отчете о покрытии. Макросы, описанные в этом вопросе, послужили источником вдохновения для этого подхода (в основном это их автоматическая версия, которая не загромождает исходный код).
Пока я использовал его только в своем собственном коде, но он должен обобщать. Вопросы и запросы на получение приветствуются!
Хорошо, так как я не очень разбираюсь в GCC, вот утомительное и очень трудоемкое решение, но, по крайней мере, оно работает!:)
Этот тест основан на том факте, что некоторые ошибки в коде шаблона не обнаруживаются до фактического создания экземпляра, то есть когда зависимые имена на самом деле не существуют в параметре шаблона:
template<class T>
struct Example{
typedef typename T::_123344_non_existent_type instantiation_test;
};
Добавьте такой typedef к каждому имеющемуся шаблону, затем скомпилируйте. Удалите его из каждой структуры / класса / функции, для которой отображается компилятор, и для каждого шаблона, который все еще содержит такую typedef, когда код, наконец, компилируется, никогда не создается. Или вам не повезло, и некоторые типы определяют такой _123344_non_existent_type
хотя я бы линчевала сотрудника, который за это отвечает.;)