測試最重要的是要有斷言。C++標(biāo)準(zhǔn)支持的編譯期斷言只有一個(gè):static_assert
。
static_assert
是C++11標(biāo)準(zhǔn)引入的一個(gè)新關(guān)鍵字,用于在編譯期做靜態(tài)斷言。它需要兩個(gè)參數(shù),第一個(gè)是一個(gè)可以在編譯期返回bool值的常量表達(dá)式,第二個(gè)是一個(gè)字符串常量,用于當(dāng)斷言失敗時(shí)編譯器輸出用。
static_assert(sizeof(int) < 1, "Invoke an assertion failure!");
當(dāng)編譯器編譯到如上代碼時(shí)會(huì)產(chǎn)生一個(gè)編譯錯(cuò)誤,在錯(cuò)誤報(bào)告中會(huì)包含字符串“Invoke an assertion failure!”。
在前面的介紹中,我們已經(jīng)將模板元編程的計(jì)算對象和返回結(jié)果都統(tǒng)一成類型了,所以我們需要對static_assert
進(jìn)行封裝,讓其能夠直接對類型進(jìn)行斷言。
首先我們需要一個(gè)斷言ASSERT_TRUE()
,它可以針對返回BoolType
的元編程表達(dá)式進(jìn)行斷言。例如可以如下使用:
ASSERT_TRUE(__bool(true));
ASSERT_TRUE(__not(__false()));
ASSERT_FALSE(__or(__true(), __false()));
我們需要用static_assert
實(shí)現(xiàn)ASSERT_TRUE
,就需要對ASSERT_TRUE
的入?yún)⒄{(diào)用__value()
元函數(shù)求值。如下是ASSERT_TRUE
的實(shí)現(xiàn):
// "tlp/test/details/Asserter.h"
#define ASSERT_TRUE(T) \
static_assert(__value(T), "Assert Failed: expect "#T" be true, but be false!")
同樣我們實(shí)現(xiàn)ASSERT_FALSE
如下:
// "tlp/test/details/Asserter.h"
#define ASSERT_FALSE(T) \
static_assert(!(__value(T)), "Assert Failed: expect "#T" be false, but be true!")
接下來我們實(shí)現(xiàn)用于斷言兩個(gè)類型是否相等的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庫中有一個(gè)特殊的類型NullType
,它的定義如下:
// "tlp/base/NullType.h"
struct NullType;
#define __null() NullType
NullType僅有類聲明,所以不能實(shí)例化。NullType被TLP庫用于各種計(jì)算返回的無效值中。對此有一個(gè)元函數(shù)__valid()
專門用于判斷表達(dá)式的值是否為NullType。
template<typename T>
struct Valid
{
using Result = __true();
};
template<>
struct Valid<NullType>
{
using Result = __false();
};
#define __valid(...) typename Valid<__VA_ARGS__>::Result
當(dāng)然你也可以對其擴(kuò)展,通過定義Valid的不同特化,來支持更多的錯(cuò)誤類型。
對此,TLP提供了斷言ASSERT_VALID
和ASSERT_INVALID
,專門用于判斷表達(dá)式是否有效:
// "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!")