測試最重要的是要有斷言。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!")