测试最重要的是要有断言。C++标准支持的编译期断言只有一个:static_assert
。
static_assert
是C++11标准引入的一个新关键字,用于在编译期做静态断言。它需要两个参数,第一个是一个可以在编译期返回bool值的常量表达式,第二个是一个字符串常量,用于当断言失败时编译器输出用。
static_assert(sizeof(int) < 1, "Invoke an assertion failure!");
当编译器编译到如上代码时会产生一个编译错误,在错误报告中会包含字符串“Invoke an assertion failure!”。
在前面的介绍中,我们已经将模板元编程的计算对象和返回结果都统一成类型了,所以我们需要对static_assert
进行封装,让其能够直接对类型进行断言。
首先我们需要一个断言ASSERT_TRUE()
,它可以针对返回BoolType
的元编程表达式进行断言。例如可以如下使用:
ASSERT_TRUE(__bool(true));
ASSERT_TRUE(__not(__false()));
ASSERT_FALSE(__or(__true(), __false()));
我们需要用static_assert
实现ASSERT_TRUE
,就需要对ASSERT_TRUE
的入参调用__value()
元函数求值。如下是ASSERT_TRUE
的实现:
// "tlp/test/details/Asserter.h"
#define ASSERT_TRUE(T) \
static_assert(__value(T), "Assert Failed: expect "#T" be true, but be false!")
同样我们实现ASSERT_FALSE
如下:
// "tlp/test/details/Asserter.h"
#define ASSERT_FALSE(T) \
static_assert(!(__value(T)), "Assert Failed: expect "#T" be false, but be true!")
接下来我们实现用于断言两个类型是否相等的ASSERT_EQ()
和ASSERT_NE()
:
// "tlp/test/details/Asserter.h"
#define ASSERT_EQ(T, Expected) \
static_assert(__value(__is_eq(T, Expected)), "Assert Failed: expect "#T" be equal to "#Expected"!")
#define ASSERT_NE(T, Expected) \
static_assert(!(__value(__is_eq(T, Expected))), "Assert Failed: expect "#T" be not equal to "#Expected"!")
它们的用法如下:
ASSERT_EQ(__int(0), __int(0));
ASSERT_NE(__int(0), __int(1));
ASSERT_EQ(__if(__true(), int, char), int);
ASSERT_EQ(__if(__false(), int, char), char);
TLP库中有一个特殊的类型NullType
,它的定义如下:
// "tlp/base/NullType.h"
struct NullType;
#define __null() NullType
NullType仅有类声明,所以不能实例化。NullType被TLP库用于各种计算返回的无效值中。对此有一个元函数__valid()
专门用于判断表达式的值是否为NullType。
template<typename T>
struct Valid
{
using Result = __true();
};
template<>
struct Valid<NullType>
{
using Result = __false();
};
#define __valid(...) typename Valid<__VA_ARGS__>::Result
当然你也可以对其扩展,通过定义Valid的不同特化,来支持更多的错误类型。
对此,TLP提供了断言ASSERT_VALID
和ASSERT_INVALID
,专门用于判断表达式是否有效:
// "tlp/test/details/Asserter.h"
#define ASSERT_VALID(T) \
static_assert(__value(__valid(T)), "Assert Failed: expect "#T" be valid, but be invalid!")
#define ASSERT_INVALID(T) \
static_assert(!(__value(__valid(T))), "Assert Failed: expect "#T" be invalid, but be valid!")