Arhn - архитектура программирования

Как модульно протестировать преднамеренные ошибки компиляции кода шаблона

Обратите внимание, что это НЕ дубликат Как написать модульный тест для проверки ошибки компиляции? поскольку я не беспокоюсь о проверке правильности внешних библиотек или самого компилятора.

Для C++ характерно, особенно при работе с шаблонами, использование методов, предотвращающих компиляцию определенного фрагмента кода. Поскольку они могут быть запутанными, как лучше всего убедиться, что определенные фрагменты кода действительно генерируют ошибки компилятора?

Поскольку тест даже не должен быть скомпилирован, вы не можете полагаться на такие вещи, как boost-test, значит, его нужно интегрировать в систему сборки? Как обычно решаются эти вопросы?


  • Я не понимаю... не могли бы вы привести пример того, что вы хотите проверить? 02.09.2011
  • Например, что класс шаблона не должен быть экземпляром, если для определенного типа нет доступной специализации, или что конкретный метод не должен быть доступен, если тип не соответствует определенным требованиям, или в общем тестировании эффективности boost::enable_if и т. д. . 02.09.2011
  • Просто напишите тестовый пример, который не должен компилироваться, затем проверьте, что он не скомпилировался... возможно, найдите соответствующее сообщение об ошибке, чтобы убедиться, что он не скомпилировался по правильной причине. 02.09.2011
  • Вы говорите, что Boost.Test — это не то, что вам нужно, но регрессионные тесты, скажем, для MPL делают именно то, что вам нужно. Вы проверяли, как они настраиваются и работают? 02.09.2011
  • @Nicola: нет, спасибо за подсказку 02.09.2011
  • возможный дубликат ошибки времени компиляции модульного теста 02.09.2011
  • Я играл с идеей настроить мою систему сборки таким образом, чтобы определенные файлы игнорировались, если они не смогли скомпилироваться, а затем написать квази-юнит-тесты, которые просто немедленно терпят неудачу (TEST(false, "This wasn't supposed to compile :(")). Если компиляция прошла успешно, она добавляется в список модульных тестов, и мы получаем ошибку. Если компиляция не удалась, система сборки просто игнорирует ошибку и продолжает работу. Однако я еще не разобрался с ним в деталях, и даже тогда он, вероятно, не будет работать для всех сред тестирования. 08.10.2011

Ответы:


1

Делайте это так же, как пишутся тесты компилятора. У вас будет немного тестового кода на каком-то языке сценариев (shell, perl, tcl и т. д.), который запустит компилятор на заданных фрагментах кода и проверит, скомпилировались ли правильные, а правильные нет.

  • gcc использует DejaGnu, созданный поверх expect, который сам построен на основе Tcl.
  • Если вы используете сценарий оболочки (вероятно, проще, DejaGnu, вероятно, излишен), вы можете взглянуть на shUnit2 .
  • Система Perl Test::Harness должна быть простой в использовании как есть.
  • В конце концов, запустить процесс из C++ не так уж и сложно, поэтому написать функцию, которая попытается вызвать компилятор для заданной строки и проверить, выводит ли он ошибку для строки, где вы ожидаете, это будет не так сложно, и чем вы можете интегрировать его в другие тесты на основе boost.test.
02.09.2011

2

Тестирование на negative feature, следовательно, гарантия того, что определенная конструкция не сможет скомпилироваться, возможно с использованием выражений c++20 requires следующим образом:

Простой пример

Ниже я проверяю, существуют ли перегрузки функции func в статических утверждениях, при использовании с тестовой средой логическое значение должно использоваться в одном из тестов времени выполнения, чтобы не блокировать компиляцию других тестов:

#include <concepts>
/// Arbitrary restrictions in order to test:
/// if T != double -> zero args
template <typename T> void func(){};
/// if T == double -> arbitrary args.
template<std::same_as<double> ...T> void func(T... as){};
template <typename T, typename... a> constexpr bool applies_to_func = requires(a... as) {
  func<T>(as...);
};
/// compiles:
static_assert(applies_to_func<int>);
static_assert(applies_to_func<double, double>);
static_assert(applies_to_func<double, double, double>);
/// function fails to compile:
static_assert(!applies_to_func<int, int>);

Код доступен в Compiler Explorer: https://godbolt.org/z/direWo.

C++17

Недавно я попытался сделать нечто подобное для проекта, в котором я могу использовать только С++ 17. В моем коде я также проверяю, соответствует ли возвращаемый тип функции ожиданиям вызывающей стороны. Помимо некоторых ограничений, касающихся нетиповых параметров шаблона, можно добиться аналогичного результата, как показано ниже. В этом случае я не мог использовать double в качестве входных данных для перегрузки из-за неявного преобразования, applies_to_func(void, int, int) будет оцениваться как true в приведенном ниже фрагменте кода.

#include <utility>
#include <string>

/// Create compile-time tests that allow checking a specific function's type  
#define COMPILE_TIME_TEST(func) COMPILE_TIME_TEST_FUNCTION(func, func)
#define COMPILE_TIME_TEST_FUNCTION(name, func)                                                              \
namespace detail {                                                                                          \
  template<typename R, auto... args> struct name ## FromArgs:std::false_type{};                             \
  template<auto... args> struct name ## FromArgs<decltype(func(args...)), args...> : std::true_type{};      \
  template<typename R, typename... Args> struct name ## FromType:std::false_type{};                         \
  template<typename... Args> struct name ## FromType<decltype(func(std::declval<Args>()...)), Args...> : std::true_type{};\
}                                                                                                           \
template<typename R, auto ...Args>                                                                          \
static constexpr auto name ## _compiles = detail::name ## FromArgs<R, Args...>::value;                      \
template<typename ...Args> \
static constexpr auto name ## _compiles_from_type = detail::name ## FromType<Args...>::value;\

int func();
template <typename T> void func(T);
void func(double);
void func(double, double );
void func(double, double, double);

// create the structs from above macros for the function `func`
COMPILE_TIME_TEST(func);

static_assert(!func_compiles<void>);
static_assert(func_compiles<int>);
static_assert(func_compiles_from_type<void, double, double>);
static_assert(!func_compiles_from_type<void, double, double, double, double>);

static_assert(func_compiles<void, 1>);
static_assert(!func_compiles<void, 1, nullptr>);

26.03.2020

3

Вам придется полагаться на внешнюю структуру для запуска набора тестов компиляции, например. makefiles или задания hudson и проверьте вывод компилятора или артефакты компилятора. Если предполагается, что компиляция завершится ошибкой, то для компилируемого файла не должно быть объектного файла. Я предполагаю, что вы могли бы написать плагин для hudson, чтобы сделать это, или простой пакетный скрипт, который запускает make-файл, который компилирует все тестовые файлы, которые должны завершиться неудачей или успехом, и соответствующим образом отметить успехи или неудачи.

В простейшем случае вы просто проверяете наличие файла '.o', чтобы увидеть, был ли ваш тест успешным, в более сложных случаях вы можете посмотреть на вывод компилятора и убедиться, что ошибка, которая возникает, совпадает с ошибкой что вы ожидаете. Это будет зависеть от компилятора, который вы используете.

Переход на один уровень глубже, вероятно, будет означать написание расширения компилятора для этого (LLVM может справиться с тем, что вы просите)

02.09.2011
  • Определенно не отдельные задания Jenkins (имя Хадсон застряло в Oracle). Мы говорим о 10-строчных фрагментах. 02.09.2011

  • 4

    Возможно, вы захотите ознакомиться с metatest — платформа модульного тестирования для шаблонных метапрограмм C++< /a> (исходное сообщение автора в списке рассылки Boost ). Получите его здесь.
    Публикации, связанные с библиотеками здесь.

    06.10.2011
  • Как это проверяет, что какой-то код не компилируется? 17.10.2016
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..