Модульный тест: проверяет, что некоторые выражения не должны быть скомпилированы - PullRequest
0 голосов
/ 14 февраля 2019

В модульных тестах я хочу проверить, что некоторые выражения не могут быть скомпилированы.Например, если я пишу не копируемый класс, я хочу проверить, что конструктор копирования не может быть вызван (поэтому выражение, которое вызывает конструктор копирования, не должно компилироваться).В настоящее время я использую тест Google, который не имеет такой возможности.

Известно, что это можно сделать с помощью SFINAE.Основная идея заключается в том, что выражение, которое должно быть проверено, должно быть передано в decltype () в качестве аргумента.И некоторая часть выражения должна быть шаблоном (без использования аргумента шаблона компилятор сразу указывает на ошибку).Теперь, если выражение может быть скомпилировано, decltype () может определить тип выражения, и будет выбран конкретный шаблон (в котором аргументы записываются в decltype).Если выражение не может быть скомпилировано, будет выбран другой шаблон.Позже, во время выполнения, мы можем проанализировать результат ...

Основным недостатком этого метода является то, что часть выражения должна быть аргументом шаблона, что приводит к плохой читаемости кода.Вопрос, который я хочу задать: где метод, чтобы написать целое выражение как есть, не разбивая его на параметр шаблона и само выражение?И еще один вопрос, есть ли возможность избежать использования std :: declval?

Например, в настоящее время я должен написать:

COMPILE_TEST(..., T, noncopyable, T(std::declval<T>()));

Если аргумент шаблона не был необходим, я мог бы тогданаписать:

COMPILE_TEST(..., noncopyable(std::declval<noncopyable>()));

И в идеальном случае я хочу написать что-то вроде этого:

COMPILE_TEST(..., { noncopyable t; noncopyable t2(t); });

Но я думаю, это совершенно невозможно.

Полный пример (https://coliru.stacked -crooked.com / a / ecbc42e7596bc4dc ):

#include <stdio.h>
#include <utility>
#include <vector>

template <typename...>  using _can_compile = void;
struct cant_compile { constexpr static bool value = false; };

#if COMPILE_TEST_ASSERTS
#define CAN_COMPILE_ASSERT(val, name, param, decl, ...) \
        static_assert(val, "this shoul'd not be compiled (" #name "): " __VA_ARGS__ " [with " #param "=" #decl "]");
#else
#define CAN_COMPILE_ASSERT(name, param, decl, ...) static_assert(true, "")
#endif

#define COMPILE_TEST(name, param, decl, ...) \
        template <typename T, typename = void> struct can_compile_##name : public cant_compile {}; \
        template <typename T> struct can_compile_##name<T, _can_compile<decltype(__VA_ARGS__)>> { constexpr static bool value = true; }; \
        CAN_COMPILE_ASSERT(!can_compile_##name<decl>::value, name, param, decl, #__VA_ARGS__); \
        constexpr bool name = can_compile_##name<decl>::value;



struct noncopyable_good
{
        noncopyable_good() {}
        noncopyable_good(const noncopyable_good&) = delete;
};

struct noncopyable_bad
{
        noncopyable_bad() {}
        // noncopyable_bad(const noncopyable_bad&) = delete;
};


COMPILE_TEST(good, T, noncopyable_good, T(std::declval<T>()));
COMPILE_TEST(bad, T, noncopyable_bad, T(std::declval<T>()));

int main()
{  
    printf("noncopyable_good can%s be copied\n", good ? "" : "'t");
    printf("noncopyable_bad can%s be copied\n", bad ? "" : "'t");
    return 0;
}

Подобные вопросы:

1 Ответ

0 голосов
/ 14 февраля 2019

Вот очень небольшая модификация вашего кода, которая устраняет дополнительный параметр.

template <class X, class Y>
auto my_declval() -> Y;

#define MY_DECLVAL(...) my_declval<T, __VA_ARGS__>()

template <typename...>  using _can_compile = void;
struct cant_compile { constexpr static bool value = false; };

#if COMPILE_TEST_ASSERTS
#define CAN_COMPILE_ASSERT(val, name, decl) \
    static_assert(val, "this shoul'd not be compiled (" #name "): " #decl );
#else
#define CAN_COMPILE_ASSERT(name, decl, ...) static_assert(true, "")
#endif


#define COMPILE_TEST(name, ...) \
        template <typename T, typename = void> struct can_compile_##name : public cant_compile {}; \
        template <typename T> struct can_compile_##name<T, _can_compile<decltype(__VA_ARGS__)>> { constexpr static bool value = true; }; \
        CAN_COMPILE_ASSERT(can_compile_##name<void>::value, name, #__VA_ARGS__); \
        constexpr bool name = can_compile_##name<void>::value;



struct noncopyable_good
{
        noncopyable_good() {}
        noncopyable_good(const noncopyable_good&) = delete;
};

struct noncopyable_bad
{
        noncopyable_bad() {}
        // noncopyable_bad(const noncopyable_bad&) = delete;
};


COMPILE_TEST(good, noncopyable_good(MY_DECLVAL(noncopyable_good)));
COMPILE_TEST(bad, noncopyable_bad(MY_DECLVAL(noncopyable_bad)));

Live demo

Идея состоит в том, что выражение, которое вы должны проверитьдолжен зависеть от параметра шаблона, но параметр шаблона не обязательно должен быть связан с типом, который необходимо проверить.Таким образом, MY_DECLVAL сделан так, что он зависит от некоторого фиктивного параметра T, а фактический передаваемый аргумент - void (может быть любого типа).

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

Вам нужно иметь по крайней мере один MY_DECLVAL в желаемом выраженииПроверять.

...