超越boost: 2)static_assert

來源:互聯網
上載者:User

static_assert是我認為boost庫中依賴性最小的一個組件。當然對於很多人來說,assert的習慣都還未必能養成,更莫說static_assert,我就還是講解一下。

 

static_assert:如果傳入一個false或者0, 就拋出一個編譯錯誤。既然是要激發編譯時間的錯誤,那麼只能傳入字面常量了。不過就算是只能檢查常量的值,也可以檢查很多東西了。另外他還能起到在編譯錯誤時,提前阻止模板的深度展開,使模板編譯錯誤資訊易於理解

 

如何把一個false或者0變成編譯錯誤,筆者先拋磚引玉,給一個例子。

 

#define STATIC_ASSERT(N) int A[ (bool(N) ? 1 : -1) ]

 

//顯然當N= false或者0時,STATIC_ASSERT(N)等價於定義了一個-1長度的數組,gcc雖然支援0長度數組,負數長度顯然不支援,所以所有的編譯器都立即報錯.

 

不過至少還有2個問題要解決:定義數組顯然會帶來空間開銷,其次名字A在一個名字空域內會重複.

 

第2個問題的解決方案很簡單,把名字A拼接上行數就行了,A##__LINE__. 只要你不在一行或一個宏中反覆展開STATIC_ASSERT,行數就是不會重複的.

第1個問題的解決要考慮到方法要能在類名字空間域,全域名字空間域,函數名字空間域等等場所適用.下面有至少這幾種方法在所有編譯器中通行:

 

#define STATIC_ASSERT(N) typedef int A[ (bool(N) ? 1 : -1) ]

#define STATIC_ASSERT(N) void A( int (*arg)[ (bool(N) ? 1 : -1) ])

#define STATIC_ASSERT(N) enum {A = sizeof(int [ (bool(N) ? 1 : -1) ])}

 

 

上述方法都沒有任何時空開銷(無論是debug 版或者release版)

 

注意有的編譯器支援 C99的變長數組,如果誤傳入的N是一個變數,#define STATIC_ASSERT(N) int A[ (bool(N) ? 1 : -1) ];會導致執行階段錯誤.後面列舉的3種方法都有效防治了這個錯誤.檢查變數請使用assert.h中的assert.

 

定義的方法差不多都說完了,我們回到原來的問題,如何把一個false或者0變成編譯錯誤.遠遠不止有筆者剛才提到的負數長度數組這樣的錯誤.

 

筆者把一些常見的激發編譯錯誤的方法列舉一下,為了方便和易於閱讀,通通使用數組法來進行表示:

 

可以相容C/C++語言的方法:

錯誤1:負數長度的數組, #define STATIC_ASSERT(N) int A[ (bool(N) ? 1 : -1) ]; //錯誤1 

錯誤2:進行除"0"常數算術運算, #define STATIC_ASSERT(N) int A[ (int(N) / int(N)) ]; //錯誤2

錯誤3:函數調用參數不匹配.如下:

size_t ttt(size_t(*arg)[ size_t(true) + 1 ]);

#define STATIC_ASSERT(N) int A[sizeof(ttt(size_t(*)[ size_t(N) + 1 ](NULL)))]

 

筆者挖空心思,在c語言下對常數輸入的限制就只找到這3個了.第3個其實已經很勉強了,另外利用邏輯運算短路特性在後半段塞入語法錯誤的通用性不好. 歡迎大家集思廣益,多多賜教!

 

只能用在c++的方法,自然都是和模板相關的了,利用模板參數可以是常整數這點.一般手法都是通過只特化模板參數值為true或者false的類來激發訪問錯誤. 往往篇幅過長,筆者就不贅述了:

錯誤3:試圖執行個體化一個已經聲明,但沒有定義的類.c++ modern design和boost就用的是這個.

錯誤4:試圖訪問一個不存在的類子定義或者靜態常量. 

錯誤5:試圖無限遞迴執行個體化.

錯誤6:試圖讓某類自己繼承自己.

錯誤7:試圖讓某類繼承另外一個類2次. 如struct A: public B, public B{};

錯誤8:試圖讓某個對象轉換成一個非父類的類型.

錯誤9:試圖讓某類擁有自己類型的成員.

........實在是太多了,我想c++編譯器作者比我更有發言權,c++就是一種給你無限種可能的語言.

 

事實上,很多static_assert還希望能夠在編譯錯誤中顯示定製的特定資訊,自然又得在名字A上面下功夫了,而且顯然只能讓這些資訊添加在變數名字或者自訂類型名字中, 利用編譯錯誤會列印這些變數或者類型的資訊.並且這些資訊中不能有中文,不能有除了'_'以外的一切標點,不能有空格.這裡筆者貼上自己的代碼給大家看看吧.

注意我把產生錯誤, 錯誤的利用, 訊息的格式幾個部分分開在幾個宏裡了.另外把調試版和非調試版分開了.

 

 

#define JOIN(x,y) x ## y<br />#define JOIN_1(x,y) JOIN(x, y)<br />#define JOIN_2(x,y) JOIN_1(x, y)<br />/*It's the begin of all error message.*/<br />#define ERRORMSG_BEGIN ATTENTION__The_assertion_error_is__<br />/*<br />*To fetch a error if testnum==0||testnum==false, by divide 0.<br />*Cast to int to avoid warning caused by bool division.<br />*/<br />#define IF_ERROR(testnum) (((int)(testnum))/((int)(testnum)))<br />/*<br />*The errormsg only support following characters: [a-zA-Z0-9] and '_'.<br />*Because it's a part of variable's name.<br />*Example:STATIC_ASSERT(sizeof(int) <= sizeof(short), The_sizeof_int_is_larger_than_short__)<br />*For above example, (Most) compilers can print error include following message:<br />ATTENTION__The_assertion_error_is__The_sizeof_int_is_larger_than_short__234<br />*The number in the end is the linenumber of the position of the macro been called.<br />*/<br />#ifndef NDEBUG<br />#ifndef _MSC_VER<br />#define STATIC_ASSERT(testnum, errormsg) static char JOIN_2(JOIN_1(ERRORMSG_BEGIN,errormsg),__LINE__)[IF_ERROR(testnum)]<br />#else<br />#define STATIC_ASSERT(testnum, errormsg) static char JOIN_1(ERRORMSG_BEGIN,errormsg)[IF_ERROR(testnum)]<br />#endif<br />#else<br />#define STATIC_ASSERT(testnum, errormsg)<br />#endif

實際的編譯錯誤中往往只有定義了一個變數,(多數)編譯器的才肯列印它的名字.typedef,或者函式宣告似乎都不會列印對應的名字,所以只好理論服從實際咯.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.