如何编写可运行的static_assert测试? [英] How to write runnable tests of static_assert?

查看:73
本文介绍了如何编写可运行的static_assert测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为包含 static_assert s的源代码库编写单元测试套件。
在设计方面,我想保证这些 static_assert 所做的工作不多于且不少于
。因此,我希望能够对其进行测试。

I am writing a unit-test suite for a source code library that contains static_asserts. I want to provide assurance these static_asserts do no more and no less than they are desired to do, in design terms. So I would like to be able to test them.

我当然可以添加导致静态断言的接口的不可编译单元测试 s
被各种各样的手段所侵犯,然后用我个人的
注释或 #if 0 全部删除向用户保证,如果其中的任何一个未注释,那么他们将观察到
该库未编译。

I could of course add uncompilable unit tests of the interface that cause the static asserts to be violated by a comprehensive variety of means, and comment or #if 0 them all out, with my personal assurance to the user that if any of them are un-commented then they will observe that the library does not compile.

但这太荒谬了。相反,我想让
一些设备在单元测试套件的上下文中将
a static_assert 替换为等效的运行时。例外,
测试框架可以捕获并有效报告:此代码在实际构建中将具有 static_assert ed

But that would be rather ridiculous. Instead, I would like to have some apparatus that would, in the context of the unit-test suite, replace a static_assert with an equivalently provoked runtime exception, that the test framework could catch and report in effect: This code would have static_asserted in a real build.

我忽略了一些明显的理由,为什么这将是一个愚蠢的主意?

Am I overlooking some glaring reason why this would be a daft idea?

如果没有,怎么办?宏工具是一种显而易见的方法,我不排除
的缺点。但也许也最好使用模板专业化或
SFINAE方法?

If not, how might it be done? Macro apparatus is an obvious approach and I don't rule it out. But maybe also, and preferably, with a template specialization or SFINAE approach?

推荐答案

出于我对这个问题的兴趣,我为自己准备了一个答案
,头文件基本上是这样的:

As I seem to be a lone crank in my interest in this question I have cranked out an answer for myself, with a header file essentially like this:

exceptionalized_static_assert.h

#ifndef TEST__EXCEPTIONALIZE_STATIC_ASSERT_H
#define TEST__EXCEPTIONALIZE_STATIC_ASSERT_H

/* Conditionally compilable apparatus for replacing `static_assert`
    with a runtime exception of type `exceptionalized_static_assert`
    within (portions of) a test suite.
*/
#if TEST__EXCEPTIONALIZE_STATIC_ASSERT == 1

#include <string>
#include <stdexcept>

namespace test {

struct exceptionalized_static_assert : std::logic_error
{
    exceptionalized_static_assert(char const *what)
    : std::logic_error(what){};
    virtual ~exceptionalized_static_assert() noexcept {}
};

template<bool Cond>
struct exceptionalize_static_assert;

template<>
struct exceptionalize_static_assert<true>
{
    explicit exceptionalize_static_assert(char const * reason) {
        (void)reason;
    }
};


template<>
struct exceptionalize_static_assert<false>
{
    explicit exceptionalize_static_assert(char const * reason) {
        std::string s("static_assert would fail with reason: ");
        s += reason;
        throw exceptionalized_static_assert(s.c_str());
    }
};

} // namespace test

// A macro redefinition of `static_assert`
#define static_assert(cond,gripe) \
    struct _1_test \
    : test::exceptionalize_static_assert<cond> \
    {   _1_test() : \
        test::exceptionalize_static_assert<cond>(gripe){}; \
    }; \
    _1_test _2_test

#endif // TEST__EXCEPTIONALIZE_STATIC_ASSERT == 1

#endif // EOF

此标头仅包含在测试套件中,然后它将使
可见 static_assert 仅在构建测试套件
时可见

This header is for inclusion only in a test suite, and then it will make visible the macro redefinition of static_assert visible only when the test suite is built with

`-DTEST__EXCEPTIONALIZE_STATIC_ASSERT=1`    

可以使用玩具模板库来草绘此设备的使用情况:

The use of this apparatus can be sketched with a toy template library:

my_template.h

#ifndef MY_TEMPLATE_H
#define MY_TEMPLATE_H

#include <type_traits>

template<typename T>
struct my_template
{
    static_assert(std::is_pod<T>::value,"T must be POD in my_template<T>");

    explicit my_template(T const & t = T())
    : _t(t){}
    // ...
    template<int U>
    static int increase(int i) {
        static_assert(U != 0,"I cannot be 0 in my_template<T>::increase<I>");
        return i + U;
    }
    template<int U>
    static constexpr int decrease(int i) {
        static_assert(U != 0,"I cannot be 0 in my_template<T>::decrease<I>");
        return i - U;
    }
    // ...
    T _t;
    // ...  
};

#endif // EOF

尝试想象代码足够大而复杂,您
不能一the而就,只需对其进行勘测并找出 static_assert s,而
会让自己满意,您知道为什么他们在那里,他们实现了设计目标。您信任回归测试。

Try to imagine that the code is sufficiently large and complex that you cannot at the drop of a hat just survey it and pick out the static_asserts and satisfy yourself that you know why they are there and that they fulfil their design purposes. You put your trust in regression testing.

这里是 my_template.h 的玩具回归测试套件:

Here then is a toy regression test suite for my_template.h:

test.cpp

#include "exceptionalized_static_assert.h"
#include "my_template.h"
#include <iostream>

template<typename T, int I>
struct a_test_template
{
    a_test_template(){};
    my_template<T> _specimen;
    //...
    bool pass = true;
};

template<typename T, int I>
struct another_test_template
{
    another_test_template(int i) {
        my_template<T> specimen;
        auto j = specimen.template increase<I>(i);
        //...
        (void)j;
    }
    bool pass = true;
};

template<typename T, int I>
struct yet_another_test_template
{
    yet_another_test_template(int i) {
        my_template<T> specimen;
        auto j = specimen.template decrease<I>(i);
        //...
        (void)j;
    }
    bool pass = true;
};

using namespace std;

int main()
{
    unsigned tests = 0;
    unsigned passes = 0;

    cout << "Test: " << ++tests << endl;    
    a_test_template<int,0> t0;
    passes += t0.pass;
    cout << "Test: " << ++tests << endl;    
    another_test_template<int,1> t1(1);
    passes += t1.pass;
    cout << "Test: " << ++tests << endl;    
    yet_another_test_template<int,1> t2(1);
    passes += t2.pass;
#if TEST__EXCEPTIONALIZE_STATIC_ASSERT == 1
    try {
        // Cannot instantiate my_template<T> with non-POD T
        using type = a_test_template<int,0>;
        cout << "Test: " << ++tests << endl;
        a_test_template<type,0> specimen;

    }
    catch(test::exceptionalized_static_assert const & esa) {
        ++passes;
        cout << esa.what() << endl;
    }
    try {
        // Cannot call my_template<T>::increase<I> with I == 0
        cout << "Test: " << ++tests << endl;
        another_test_template<int,0>(1);
    }
    catch(test::exceptionalized_static_assert const & esa) {
        ++passes;
        cout << esa.what() << endl;
    }
    try {
        // Cannot call my_template<T>::decrease<I> with I == 0
        cout << "Test: " << ++tests << endl;
        yet_another_test_template<int,0>(1);
    }
    catch(test::exceptionalized_static_assert const & esa) {
        ++passes;
        cout << esa.what() << endl;
    }
#endif // TEST__EXCEPTIONALIZE_STATIC_ASSERT == 1
    cout << "Passed " << passes << " out of " << tests << " tests" << endl;
    cout << (passes == tests ? "*** Success :)" : "*** Failure :(") << endl; 
    return 0;
}

// EOF

您可以编译 test.cpp 至少具有gcc 6.1,clang 3.8和选项
-std = c ++ 14 ,或VC ++ 19.10.24631.0和选项 / std:c ++ latest 。首先不要定义 TEST__EXCEPTIONALIZE_STATIC_ASSERT
(或将其定义为0)。然后运行,输出应为:

You can compile test.cpp with at least gcc 6.1, clang 3.8 and option -std=c++14, or VC++ 19.10.24631.0 and option /std:c++latest. Do so first without defining TEST__EXCEPTIONALIZE_STATIC_ASSERT (or defining it = 0). Then run and the the output should be:

Test: 1
Test: 2
Test: 3
Passed 3 out of 3 tests
*** Success :)

如果然后重复,但使用 -DTEST__EXCEPTIONALIZE_STATIC_ASSERT = 1 进行编译,

If you then repeat, but compile with -DTEST__EXCEPTIONALIZE_STATIC_ASSERT=1,

Test: 1
Test: 2
Test: 3
Test: 4
static_assert would fail with reason: T must be POD in my_template<T>
Test: 5
static_assert would fail with reason: I cannot be 0 in my_template<T>::increase<I>
Test: 6
static_assert would fail with reason: I cannot be 0 in my_template<T>::decrease<I>
Passed 6 out of 6 tests
*** Success :)

很显然在静态断定
测试用例中, try / catch 块的重复编码是乏味的,但是在设置真实且受人尊敬的
单元的情况下,测试框架人们会希望它包装异常测试设备以生成您看不见的东西。例如,在googletest中,您可以编写以下内容:

Clearly the repetitious coding of try/catch blocks in the static-assert test cases is tedious, but in the setting of a real and respectable unit-test framework one would expect it to package exception-testing apparatus to generate such stuff out of your sight. In googletest, for example, you are able to write the like of:

TYPED_TEST(t_my_template,insist_non_zero_increase)
{
    ASSERT_THROW(TypeParam::template increase<0>(1),
        exceptionalized_static_assert);
}

现在我可以回到世界末日的计算:)

Now I can get back to my calculations of the date of Armageddon :)

这篇关于如何编写可运行的static_assert测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆