Возможно ли ASSERT_DOES_NOT_COMPILE с GTest?

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

template<int N>
class A
{
public:

    A() {
        static_assert(N > 0, "N needs to be greater 0.");
    }

};

Можно ли создать модульный тест googletest, который компилируется, но сообщает об ошибке во время выполнения? Например:

TEST(TestA, ConstructionNotAllowedWithZero)
{
    ASSERT_DOES_NOT_COMPILE( 
        {
            A< 0 > a;
        }
    );
}

person Dimitri Schachmann    schedule 24.07.2015    source источник
comment
Я думаю, что возможное решение не может быть напрямую связано с вашей структурой модульного тестирования. Видели ли вы как ожидать сбоя static_assert и справляться с ним с помощью Boost. Платформа тестирования? или Как написать исполняемые тесты static_assert?   -  person Florian    schedule 24.07.2015
comment
Спасибо, но эти ссылки также не дают удовлетворительного ответа.   -  person Dimitri Schachmann    schedule 28.07.2015
comment
Просто случайная глупая идея: #define static_assert assert в верхней части тестового файла. Некрасиво, но, возможно, это то, что вы хотите.   -  person Lærne    schedule 15.10.2015


Ответы (1)


Есть способ, но, к сожалению, это, вероятно, не тот способ, которым вы хотите.

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

#define CODE { \
 utter garbage \
}
struct test
{
    template<class T>
    static std::false_type try_compile(...) { return{}; }
    template<class T>
    static auto try_compile(int)
    -> decltype([]() CODE, void(), std::true_type());
    { return {}; }
};
struct tag {};
using does_compile = decltype(test::try_compile<tag>(0));

выход:

./maybe_compile.cpp:88:17: error: lambda expression in an unevaluated operand
    -> decltype([]() CODE, void(), std::true_type());

Итак, мы вернулись к чертежной доске и старому доброму системному вызову для обращения к компилятору...

#include <iostream>
#include <string>
#include <cstdlib>
#include <fstream>
#include <sstream>

struct temp_file {
    temp_file()
    : filename(std::tmpnam(nullptr))
    {}

    ~temp_file() {
        std::remove(filename.c_str());
    }

    std::string filename;
};

bool compiles(const std::string code, std::ostream& reasons)
{
    using namespace std::string_literals;

    temp_file capture_file;
    temp_file cpp_file;

    std::ofstream of(cpp_file.filename);
    std::copy(std::begin(code), std::end(code), std::ostream_iterator<char>(of));
    of.flush();
    of.close();
    const auto cmd_line = "c++ -x c++ -o /dev/null "s + cpp_file.filename + " 2> " + capture_file.filename;
    auto val = system(cmd_line.c_str());

    std::ifstream ifs(capture_file.filename);
    reasons << ifs.rdbuf();
    ifs.close();

    return val == 0;
}

auto main() -> int
{
    std::stringstream reasons1;
    const auto code1 =
R"code(
    #include <iostream>
    int main() {
        return 0;
    }
)code";
    std::cout << "compiles: " << compiles(code1, reasons1) << std::endl;

    std::stringstream reasons2;
    const auto code2 =
R"code(
    #include <iostream>
    int main() {
        FOO!!!!XC@£$%^&*()VBNMYGHH
        return 0;
    }
)code";
    std::cout << "compiles: " << compiles(code2, reasons2) << std::endl;
    std::cout << "\nAnd here's why...\n";
    std::cout << reasons2.str() << std::endl;

    return 0;
}

который в моем случае дает следующий пример вывода:

compiles: 1
compiles: 0

And here's why...
/var/tmp/tmp.3.2dADZ7:4:9: error: use of undeclared identifier 'FOO'
        FOO!!!!XC@£$%^&*()VBNMYGHH
        ^
/var/tmp/tmp.3.2dADZ7:4:19: error: non-ASCII characters are not allowed outside of literals and identifiers
        FOO!!!!XC@£$%^&*()VBNMYGHH
                  ^
2 errors generated.

конечно, вы можете добавить все необходимые макросы вокруг вызова compiles(), чтобы его GTESTify. Вам, конечно, придется установить параметры командной строки при вызове c-компилятора, чтобы установить правильные пути и определения.

person Richard Hodges    schedule 24.07.2015
comment
Спасибо за ваши усилия. Хорошая идея использовать системный вызов компилятора. :D Но ты прав: это не то, чего я хочу. Я начинаю сомневаться, что это вообще возможно. - person Dimitri Schachmann; 28.07.2015
comment
Это невозможно, потому что нет возможности оценить код в невычисленном контексте. (Вы видите дихотомию?) - person Richard Hodges; 28.07.2015
comment
хороший ответ, только небольшая придирка: ваша линия рассуждений может немного вводить в заблуждение, потому что проблема в вопросе не в лямбде, а в том, что для запуска static_assert необходимо вызывать конструктор, а это не очень хорошо работает с SFINAE. Мне пришлось убедить себя (godbolt.org/z/aVaxrg), и я думаю, что это невозможно использовать черта для обнаружения этого утверждения - person 463035818_is_not_a_number; 08.07.2020