Отсутствует покрытие llvm-cov при вызове из утверждения

У меня есть следующие файлы заголовков:

power.hpp:

#pragma once

#include <type_traits>

template <typename T, typename R = decltype(std::declval<T>() * std::declval<T>())>
constexpr inline R square(const T& x_) noexcept;

power.inl:

#pragma once

#include "power.hpp"

template <typename T, typename R>
constexpr inline R square(const T& x_) noexcept
{
    return (x_ * x_);
}

power_unit_test.cpp:

#include <power.inl>

int main()
{
    static_assert(square(2) == 4);
    assert(square(2) == 4);
    square(2);

    return (0);
}

После компиляции с флагами -fprofile-instr-generate а также -fcoverage-mapping в использовании Clang++. Запустив двоичный файл модульного теста, я получаю отчет о том, что каждая из трех строк в main была вызвана, но содержимое функции использовалось только один раз. Это использование от отдельного вызова square(2)Утверждается, что утверждения не дают правильных отчетов о покрытии.

Если я удалю автономный square(2) тогда охват не достигает 100%, так как утверждения по какой-то причине пропускают создание покрытия.

Отчет о покрытии гласит:

power.inl:

   22|       |        template <typename T, typename R>
   23|       |        constexpr inline R square(const T& x_) noexcept
   24|      0|        {
   25|      0|            return (x_ * x_);
   26|      0|        }

power_unit_test.cpp

   29|       |int main()
   30|      1|{
   31|      1|    static_assert(arc::math::sq(2) == 4);
   32|      1|    assert(arc::math::sq(2) == 4);
   33|      1|    // arc::math::sq(2);
   34|      1|
   35|      1|    return (0);
   36|      1|}

Пожалуйста, не могли бы вы помочь мне понять, почему покрытие не сообщается, как я ожидал здесь? Это ошибка в llvm-cov или я не понимаю намерения покрытия?

Компиляция с использованием homebrew's clang 7.0.1 на MacOS. Использование CMake 3.13.2 для системы сборки.

1 ответ

Решение

Проблема, с которой вы сталкиваетесь, заключается в том, что ваш компилятор square() функция для обоих методов assert. Поскольку код встроен, он никогда не вызывает ваш внешний код.

Ваша первая мысль может быть удалить inline идентификатор, но это не сработает. Это потому, что ваш компилятор, скорее всего, достаточно умен, чтобы признать, что square() Функция может быть встроена и идет вперед и делает это в любом случае. Конечным результатом является то, что внешний код не вызывается.

Таким образом, вам нужен способ обойти встраивание square() функция. Вы можете сделать это с помощью указателя на функцию. Смотрите следующую модификацию вашего main функция:

int main()
{
    int (*f_ptr)(const int&);       // Ptr to func that takes 'const int&' and returns 'int' 
    f_ptr = &square;

    static_assert(square(2) == 4);  // Cant use 'f_ptr' here
    assert(f_ptr(2) == 4);
    f_ptr(2);

    return (0);
}

В приведенном выше коде мы заменяем явные вызовы square(const int&) с указателем на функцию, f_ptr, В результате компилятор не будет автоматически вставлять функции в утверждения, и код будет успешно вызван дважды. Результат:

power.cpp:

    4|       |template <typename T, typename R>
    5|       |constexpr inline R square(const T& x_) noexcept
    6|      2|{
    7|      2|    return (x_ * x_);
    8|      2|}

power_unit_test.cpp:

    5|       |int main()
    6|      1|{
    7|      1|    int (*f_ptr)(const int&);
    8|      1|    f_ptr = &square;
    9|      1|
   10|      1|    static_assert(square(2) == 4);
   11|      1|    assert(f_ptr(2) == 4);
   12|      1|    f_ptr(2);
   13|      1|
   14|      1|    return (0);
   15|      1|}

Быстрая заметка. поскольку static_assert по сути, утверждение времени компиляции, мы не можем заменить вызов square() с нашим указателем на функцию, так как указатели на функции не являются константными выражениями. Но не волнуйтесь, ваш компилятор достаточно умен, чтобы жаловаться, если вы попытаетесь заменить square(2) с указателем на функцию f_ptr(2) Вот.

Другие вопросы по тегам