C++編譯期數據結構 - 在不同的地方聲明全局數組的初始值
--Pony279
首先, 閱讀本文需要讀者需要有一定的 C++ 模板編程的基礎.
一 需求
在 C++ 里, 數組的定義和初始化這樣基本的語法是大家都知道的事情了, 例如: int array[3] = {1,2,3}; 這條語句定義了一個有3個元素的數組, 初始值分別為 1, 2, 3. 這個很簡單.
可是, 不知道有沒有人碰到一些殊的情況: 我在程序中需要用來一個個static const 類型的數組, 數組的元素的個數和各個元素的值都是可以在編譯期就確定的, 但是我可能會在寫代碼的過程中時不時的給數組加入新元素. 而在C++ 的語法里, 如果要聲明數組的初始值, 就必須集中的聲明, 就像剛才那個例子那樣. 這樣的做法對我這種懶人來說顯然是太過麻煩了, 我更希望的是, 我可以在某一個代碼段聲明數組的初始值. 然后在寫一個函數或者幾個函數聲明, 然后我突然又想給這個數組加一個新元素, 我就可以直接繼續插入一行代碼.
我的想法可能用下面的這個例子說明:
GLOBAL_ARRAY_BEGIN(int, arr); // 我需要一個數組, 叫做 arr
ARRAY_ELEMENT(arr,1); // 數組的第一個元素為 1
void some_function(void){ // 現在我想寫一個函數了
// blah blah blah ….
}
ARRAY_ELEMENT(arr,2); // 我突然又想給數組添加一個元素 2
/* 用來表示數組定義結束, 理想情況下不用寫這個最好. 加上這個實現起來可能會簡
單一些 */
GLOBAL_ARRAY_END(arr);
int main(){ // 在主函數中使用這個數組
using namespace std;
cout<<"arr.length() = " <<arr.length()<<endl;
for(int i=0; i<arr.length(); i++) // 訪問數組的元素
cout<<"arr["<<i<<"] = "<<arr[i]<<endl;
}
即使是通過這樣的代碼, 我也能定義一個數組 arr, arr 里面有兩個元素, 值分別為 1 和 2. 很明顯, 這樣的用法可以方便很多, 而且在適當的時候使用, 可以很好的提高代碼的閱讀性哦!
也許你會覺得這樣的想法有點不可思議, 在我還沒解決問題之前, 也覺得這是很不可思議的, 這樣的語法明顯和 C/C++ 的基本語法有"沖突", 甚至會讓人覺得這是不可能實現的. 但是, C++ 強大之處就在於它能把不可能變成可能!
后面我將介紹這段代碼的實現, 也許在將來你並不會用上這段代碼, 但是我希望看過這篇文章后你會有所收獲.
二 簡單的解決方案
上面的代碼很明顯需要通過一層宏定義的封裝來實現, 因為我們不可能用函數來聲明編譯期的內容, 但是考慮到宏定義是 Debug un-friendly, 即很不方便調試的. 所以在測試代碼中, 可以不使用宏定義, 而是實現了基本的代碼以后再進行封裝. 又因為我們需要的數組是可以在編譯期確定的, 所以我們當然需要用模板來做這件事情啦,
但是我們要怎么通過定義數組的不同元素呢, 有模板編程經驗的人馬上就會想到類模板的特化了, 雖然具體的實現在腦子里還是很模糊, 但是有這個想法就等於有了方向了.
下面是一個基本的實現 (見附件中的 basic_impl):
2 enum{ begin_line = __LINE__ };
3 template< int > struct ARR{ };
4
5 /* 添加元素時, 可以對模板進行特化, 添加指定的數據成員
6 這里添加一個元素 1, 特化的編號可以用 __LINE__宏來產生 */
7 template<>
8 struct ARR<__LINE__>{ const int i; ARR():i( 1){}};
9
10
11 /* 再添加一個元素 */
12 template<>
13 struct ARR<__LINE__>{ const int i; ARR():i( 2){}};
14
15 enum{ end_line = __LINE__ };
16
17 /* 以二分法遞歸的方式繼承上面的所有模板, 包括特化過的和沒
18 特化過的 */
19 template< int begin, int end>
20 struct ARR_DEF: public ARR_DEF<begin,(begin+end)/ 2>
21 , public ARR_DEF<(begin+end)/ 2+ 1,end>
22 {};
23
24 template< int begin> /* 特化 ARR_DEF */
25 struct ARR_DEF<begin, begin>: public ARR<begin>
26 {};
27
28 /* 實現該數組 */
29 static const ARR_DEF<begin_line, end_line> arr;
30
31
32 /* 測試代碼 */
33 #include <iostream>
34
35 int main(){
36 using namespace std;
37
38 int len = sizeof(arr)/ sizeof( int);
39 cout<< " sizeof(arr)/sizeof(int)= "<<len<<endl;
40 const int* p = ( const int*)&arr;
41 for( int i= 0; i<len; i++) /* 打印數組的內容 */
42 cout<<p[i]<<endl;
43 }
44
45 /*
46 程序輸出結果:
47 sizeof(arr)/sizeof(int)=2
48 1
49 2
50 */
以上就是我們的想法的基本實現和測試了. 通過上面的代碼可以看出, 每添加一個數據元素, 就特化一次類模板 ARR, 在最后實現的時候, 繼承上面所有的模板, 因為只有特化過的 ARR 才有數據成員, 那么最后定義的 arr 的數據成員中也應該就只含有自己添加的元素了. 注意到我這里說的是應該而不是一定, 如果編譯器沒有做好 EBCO (empty base class optimization), 即空基類優化, 定義出來的 arr 里面就會有一些附加的沒有意義的內容. 這段代碼是在 Code::blocks+gcc 4.7.1 編譯器下測試通過的, 結果表明編譯器會進行 EBCO. 而我在 Visual Studio 2010 中測試的時候發現 arr 里面會有附加的內容, 編譯器並沒有做好 EBCO, 這也許是我工程設置上的問題吧.
三 完善和封裝代碼
上面的代碼只對我們的想法的基本的驗證, 要在實現中使用, 在把代碼封裝成宏之前我們還需要解決其中存在的問題:
1) 某些編譯器(如M$的編譯器)沒有做好空基類優化的問題
就像上面提到的, 如果編譯器沒有做好空基類優化, 那么最后實現的數組就會有附加的內容, 這樣出乎意料之外的空間分配很可能就會導致程序崩潰. 當然我很想讓代碼不依賴於編譯器的空基類優化, 這在實現起來是可能的. 但是為了保證絕對的安全, 我們可以使用 static_assert (靜態斷言) 來保證我們的代碼的數據的空間分配是正確的情況下才能編譯通過.
至於讓代碼不依賴編譯器的空基類優化, 顯然, 只要最后繼承的時候只繼承特化過的模板就行了. 而這個繼承可以分為三步去實現:
第一步, 計算出上面總的特化次數. 從上面的代碼可以看出, 只有特化過的模板才有數據元素 e, 所以我們可以通過一個 member detector 去特定檢測成員存在與否. 當然也可以使用 sizeof 操作符, 但是如果我們需要創建 char 類型的編譯期數組的話這個方法就行不通了, 因為即使是空類型, 對其使用 sizeof 操作符也會返回 1 而不是 0.
注: member detector 在 wiki 的開源圖書 more c++ idioms 中有討論, 可以參考 http://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector
特化次數的統計的實現如下(看附件中的 specialization_count.cpp):
/** @brief 創建特化計數器.
* @param type 模板的類型. 該模板類型只能接收一個 int 類型的
* 參數.
* @param member type 模板特化后特有的成員
* @param name 計數器的名字.
*/
#define SPECIALIZATION_COUNTER_CREATE(type,member,name) \
template <int begin, int end> \
struct name{ \
enum { \
value = name<begin,(begin+end)/2>::value \
+ name<(begin+end)/2+1,end>::value \
}; \
}; \
template <int _i> \
struct name< _i, _i>{ \
CREATE_MEMBER_DETECTOR(member); \
enum { value = DETECT_MEMBER(type<_i>,member) }; \
}
/** @brief 得到特化計數器的計數結果.
* @param n 特化計數器的名字.
* @param begin 起始特化標號.
* @param end 終止特化標號. begin 和 end 限定了查找的范圍.
*/
#define SPECIALIZATION_COUNTER_VALUE(name,begin,end) \
name<begin,end>::value
注: 這里直接貼出封裝過后的宏, 在具體實現過程中還是需要先使用基本模板, 經測試功能正常后再使用宏封裝. 測試的過程比較繁瑣, 后面也不再詳細說明這這個過程了.
其使用示例如下:
#include <iostream>
using namespace std;
template<int i> struct A{};
template<> struct A<1>{static const int i=1;};
template<> struct A<5>{static const int i=2;};
template<> struct A<7>{static const int i=3;};
SPECIALIZATION_COUNTER_CREATE(A,i,counter);
int main(){
cout<<"specialization count = "
<<SPECIALIZATION_COUNTER_VALUE(counter,0,20)
<<endl;
}
/* 程序輸出的結果為 specialization count = 3 */
第二步, 實現通過"n 號特化"(即 插入的n號元素) 中的 n 來找到具體的特化的類型.其實現如下 (使用示例也在代碼中說明清楚了, 可以看附件中的 specialization_find.cpp)
/** @def SPECIALIZATION_FINDER_CREATE
* @brief 模板的特化類型查找器. 參考 @ref SPECIALIZATION_FINDER_RESULT
* @param count 模板對應的特化計數器的名字.
* @param template_type 模板的名字.
* @param name 查找器的名字
*/
#define SPECIALIZATION_FINDER_CREATE(count,template_type,name) \
template<int index,int begin,int end> \
struct name{ \
enum { \
n = SPECIALIZATION_COUNTER_VALUE \
(count,begin,(begin+end)/2) \
}; \
template<bool , typename dummy=void> \
struct select{ \
typedef name<index, begin,(begin+end)/2> type; \
}; \
template <typename dummy> \
struct select<false,dummy>{ \
typedef name<index-n,(begin+end)/2+1,end> type; \
}; \
typedef typename select<(n>index)>::type range; \
typedef typename range::type type; \
}; \
template<int index,int name##i> \
struct name<index,name##i,name##i>{ \
typedef template_type<name##i> type; \
}
/** @def SPECIALIZATION_FINDER_RESULT
* @brief 得到特化查找器的查找結果, 結果是一個特化后的
* 類型. 例如:
* @code
* template<int i> struct T{};
* template<> struct T<3>{static const int i = 0;};
* template<> struct T<8>{static const int i = 2;};
*
* // the finder needs a counter first
* SPECIALIZATION_COUNTER_CREATE(T,i,counter);
* SPECIALIZATION_FINDER_CREATE(counter,T,finder);
*
* // get the first specialization type from T<0> to T<10>
* SPECIALIZATION_FINDER_RESULT(finder,0,0,10) a;
* // get the second specialization type from T<0> to T<10>
* SPECIALIZATION_FINDER_RESULT(finder,1,0,10) b;
*
* int main(){
* using namespace std;
* cout<<"a.i = "<<a.i<<endl
* <<"b.i = "<<b.i<<endl;
* }
* // program output
* // a.i = 0
* // b.i = 2
* @endcode
* @param name 特化查找器的名字.
* @param index 特化的索引號 (第一次特化的索引號為0).
* @param begin 查找的起始模板參數
* @param end 查找的終止模板參數
*/
#define SPECIALIZATION_FINDER_RESULT(name,index,begin,end) \
name<index,begin,end>::type
從上面的代碼可以看出查找的效率並不高, 不過話說回來, 誰讓你 M$ 的編譯器不做好空基類優化呢, 不做好優化就有得你忙的, 哼!
第三步, 既然能找到每個特化的類型, 當然也就可以選擇性的繼承了. 繼承方式和原來的類似.
2) 繼承方式以及編譯安全性問題
注意到基本實現中的ARR_DEF模板的繼承方式, 是以二分法加遞歸的方式, 這類似二叉樹的結構, 而二叉樹的結構特點就是在深度不高的情況下可以有很多葉子結點, 也就是說, 編譯器很難檢測出需要實現化的模板過多. 比如, 如果你在第一個元素和第二個元素的定義之間插入一句 #line 5000 , 編譯器就會直接陷入長期的循環, 多久能編譯完我就不知道了, 估計最少也要一個小時才能編譯完這幾行代碼吧. 為了解決這個問題, 我們可以定義一個最大的間隔數, 加利用 STATIC_ASSERT 來解決這個問題, 而且, gcc 和 MSVC 編譯器都有預定義的宏 __COUNTER__ , 使用起來會比 __LINE__ 好很多.
3) 真的是編譯期決定的嗎?
Oh, 很不幸的, 當我在 arm 編譯器中測試最原始的代碼的時候, 我發現編譯器是把數組放在 RW(讀寫) 區而不是 RO(只讀) 區, 愚蠢的 arm 編譯器(只支持ISO C++03標准)並不知道這個類型是可以優化的. 不過, 很幸運的, gcc 4.7.1 支持 C++11 的關鍵字 constexpr!!! (很可惜的是 M$ 的編譯器居然還不支持這個關鍵字). 利用這個關鍵字我們就可以告訴編譯器這個數據結構是可以在編譯期決定的了.
綜合上面的內容, 全部實現的代碼如下 (附件中的 all_together.cpp):

using namespace std;
// 為了方便把 STATIC_ASSERT 放在這里
// 也可以參考 boost 庫
template < bool x> struct STATIC_ASSERTION_FAILURE;
template <> struct STATIC_ASSERTION_FAILURE< true>{};
template< int x> struct static_assert_test{};
#define STATIC_ASSERT(B) \
typedef static_assert_test< sizeof(STATIC_ASSERTION_FAILURE<B>)> \
static_assert_typedef_##__LINE__
// 參考自 c++ template: the complete guide - 15.2.2
// Addison Wesley By David Vandevoorde, Nicolai M. Josuttis
template<typename T>
class is_class_type{
private:
typedef char SizeOne;
typedef struct { char a[ 2]; } SizeTwo;
template<typename C> static SizeOne test( int C::*);
template<typename C> static SizeTwo test(...);
public:
enum { result = sizeof(test<T>( 0)) == 1
,value = result};
};
// member detector is place here for convinience
// 可以參考 wiki book - more c++ idioms - member detector
#define CREATE_MEMBER_DETECTOR(X) \
template<typename T##X> class Detect_##X { \
struct Fallback { int X; }; \
template <typename TT##X, int > \
struct DerivedT: public TT##X, Fallback{}; \
template<typename TT##X> \
struct DerivedT<TT##X, 0>: Fallback {}; \
\
typedef DerivedT<T##X, is_class_type<T##X>::value> Derived; \
\
template<typename U, U> struct Check; \
typedef char ArrayOfOne[ 1]; \
typedef char ArrayOfTwo[ 2]; \
\
template<typename U> static ArrayOfOne & \
func(Check< int Fallback::*, &U::X> *); \
template<typename U> static ArrayOfTwo & func(...); \
public: \
enum { value = sizeof(func<Derived>( 0)) == 2 }; \
}
#define DETECT_MEMBER(TYPE,NAME) Detect_##NAME<TYPE>::value
//////////////////////////////////////////////////////////// //
// 這里的內容可以放到一個頭文件里面
#ifdef _MSC_VER /* VC 的編譯器的空基類優化做得不好 */
#define EMPTY_BASE_CLASS_OPTIMIZATION_ASSERT_FAILED
#endif
#ifndef __COUNTER__
#define __COUNTER__ __LINE__
#endif
/* * 如果定義了 CONSTEXPR_SUPPORT 表示編譯器支持 constexpr 關鍵
字 */
// #define CONSTEXPR_SUPPORT
#ifdef CONSTEXPR_SUPPORT
#define constexpr_fun constexpr
#define constexpr_obj constexpr
#else
#define constexpr_fun inline
#define constexpr_obj const
#endif
/* * @def GLOBAL_ARRAY_BEGIN
* @brief 用於建立編譯期數組, 其初始值可以在不同的地方聲明.
* 使用示例如下:
* @code
* #include <iostream>
* #include "distributed_array.h"
*
* GLOBAL_ARRAY_BEGIN(int, arr);
* ARRAY_ELEMENT(arr, 1);
*
* void fun(); // declare a function is ok.
*
* ARRAY_ELEMENT(arr, 3);
* ARRAY_ELEMENT(arr, 5);
* GLOBAL_ARRAY_END(arr);
*
* int main(){
* using namespace std;
*
* cout<<"arr.length() = " <<arr.length()<<endl;
* for(int i=0; i<arr.length(); i++)
* cout<<"arr["<<i<<"] = "<<arr[i]<<endl;
* }
* // program output:
* // arr.length() = 3
* // arr[0] = 1
* // arr[1] = 3
* // arr[2] = 5
* @endcode
*/
#define GLOBAL_ARRAY_BEGIN(ele_type,name) \
template< int,typename dummy= void> struct \
name##_ele_{}; \
template<typename _dummy> struct \
name##_ele_< 0,_dummy> \
{ \
enum { \
begin = __COUNTER__ \
}; \
typedef const ele_type type; \
constexpr_fun name##_ele_(){} \
}
#define ARRAY_ELEMENT(name, value) \
template<typename T_##name> struct \
name##_ele_<__COUNTER__ \
,T_##name> \
{ \
typedef name##_ele_< 0>::type type; \
type e; \
constexpr_fun name##_ele_():e(value){} \
}
/* * @brief 創建特化計數器.
* @param type 模板的類型. 該模板類型只能接收一個 int 類型的
* 參數.
* @param member type 模板特化后特有的成員
* @param name 計數器的名字.
*/
#define SPECIALIZATION_COUNTER_CREATE(type,member,name) \
template < int begin, int end> \
struct name{ \
enum { \
value = name<begin,(begin+end)/ 2>::value \
+ name<(begin+end)/ 2+ 1,end>::value \
}; \
}; \
template < int _i> \
struct name< _i, _i>{ \
CREATE_MEMBER_DETECTOR(member); \
enum { value = DETECT_MEMBER(type<_i>,member) }; \
}
/* * @brief 得到特化計數器的計數結果.
* @param n 特化計數器的名字.
* @param begin 起始特化標號.
* @param end 終止特化標號. begin 和 end 限定了查找的范圍.
*/
#define SPECIALIZATION_COUNTER_VALUE(name,begin,end) \
name<begin,end>::value
/* * @def SPECIALIZATION_FINDER_CREATE
* @brief 模板的特化類型查找器. 參考 @ref SPECIALIZATION_FINDER_RESULT
* @param count 模板對應的特化計數器的名字.
* @param template_type 模板的名字.
* @param name 查找器的名字
*/
#define SPECIALIZATION_FINDER_CREATE(count,template_type,name) \
template< int index, int begin, int end> \
struct name{ \
enum { \
n = SPECIALIZATION_COUNTER_VALUE \
(count,begin,(begin+end)/ 2) \
}; \
template< bool , typename dummy= void> \
struct select{ \
typedef name<index, begin,(begin+end)/ 2> type; \
}; \
template <typename dummy> \
struct select< false,dummy>{ \
typedef name<index-n,(begin+end)/ 2+ 1,end> type; \
}; \
typedef typename select<(n>index)>::type range; \
typedef typename range::type type; \
}; \
template< int index, int name##i> \
struct name<index,name##i,name##i>{ \
typedef template_type<name##i> type; \
}
/* * @def SPECIALIZATION_FINDER_RESULT
* @brief 得到特化查找器的查找結果, 結果是一個特化后的
* 類型. 例如:
* @code
* #include <iostream>
* using namespace std;
* template<int i> struct T{};
* template<> struct T<3>{static const int i = 0;};
* template<> struct T<8>{static const int i = 2;};
*
* // the finder needs a counter first
* SPECIALIZATION_COUNTER_CREATE(T,i,counter);
* SPECIALIZATION_FINDER_CREATE(counter,T,finder);
*
* // get the first specialization type from T<0> to T<10>
* SPECIALIZATION_FINDER_RESULT(finder,0,0,10) a;
* // get the second specialization type from T<0> to T<10>
* SPECIALIZATION_FINDER_RESULT(finder,1,0,10) b;
*
* int main(){
* using namespace std;
* cout<<"a.i = "<<a.i<<endl
* <<"b.i = "<<b.i<<endl;
* }
* // program output
* // a.i = 0
* // b.i = 2
* @endcode
* @param name 特化查找器的名字.
* @param index 特化的索引號 (第一次特化的索引號為0).
* @param begin 查找的起始模板參數
* @param end 查找的終止模板參數
*/
#define SPECIALIZATION_FINDER_RESULT(name,index,begin,end) \
name<index,begin,end>::type
#define ARRAY_COUNTER_MAX 200
/* * @brief 在聲明完數組的元素后, 使用這個宏來產生最終的
* 數組類型.
* @param temp_name 數組元素對應的模板的名字
* @param type_name 生成的類型的名字
*/
#define ARRAY_TYPE_CREATE(temp_name,type_name) \
struct type_name{ \
private: \
SPECIALIZATION_COUNTER_CREATE(temp_name,e,counter); \
enum{ \
begin = temp_name< 0>::begin \
, end_counter = temp_name<- 1>::end \
, current_end = (end_counter - begin) > \
ARRAY_COUNTER_MAX ? \
begin : end_counter \
, count = SPECIALIZATION_COUNTER_VALUE \
(counter,begin,current_end) \
, end = count> 0?current_end:- 1 \
}; \
template< int b, int e> \
struct arr: \
public arr<b,(b+e)/ 2> \
, public arr<(b+e)/ 2+ 1,e> \
{ \
constexpr_fun arr(){} \
}; \
template< int b> \
struct arr<b, b>: public temp_name<b> \
{ \
constexpr_fun arr(){} \
}; \
template< int b> \
struct arr<b, - 1> /* for error checking */ \
{ \
STATIC_ASSERT(b==- 1); \
}; \
arr<begin+ 1,end> a; \
typedef temp_name< 0>::type type; \
public: \
constexpr_fun type_name(){} \
constexpr_fun size_t length() const \
{ return sizeof(* this)/ sizeof(type); } \
type& operator[](size_t i) const \
{ return ((type*)( this))[i]; } \
}
#define ARRAY_TYPE_CREATE_WITHOUT_EBCO(temp_name,type_name) \
struct type_name{ \
private: \
SPECIALIZATION_COUNTER_CREATE(temp_name,e,counter); \
SPECIALIZATION_FINDER_CREATE(counter,temp_name,finder); \
enum{ \
begin_counter = temp_name< 0>::begin \
, begin = 0 \
, end_counter = temp_name<- 1>::end \
, current_end = (end_counter - begin_counter) > \
ARRAY_COUNTER_MAX ? \
begin_counter : end_counter \
, count = SPECIALIZATION_COUNTER_VALUE \
(counter,begin,current_end) \
, end = count> 0?count- 1:- 1 \
}; \
template< int b, int e> \
struct arr: \
public arr<b,(b+e)/ 2> \
, public arr<(b+e)/ 2+ 1,e> \
{ \
constexpr_fun arr(){} \
}; \
template< int b> \
struct arr<b, b>:SPECIALIZATION_FINDER_RESULT \
(finder,b,begin_counter,end_counter) \
{ \
constexpr_fun arr(){} \
}; \
template< int b> \
struct arr<b, - 1> /* for error checking */ \
{ \
STATIC_ASSERT(b==- 1); \
}; \
arr<begin,end> a; \
typedef temp_name< 0>::type type; \
public: \
constexpr_fun type_name(){} \
constexpr_fun size_t length() const \
{ return sizeof(* this)/ sizeof(type); } \
type& operator[](size_t i) const \
{ return ((type*)( this))[i]; } \
}
#define ARRAY_END_HELPER(name) \
template<typename _dummy> struct \
name <- 1,_dummy> \
{ \
enum { \
end = __COUNTER__ \
}; \
constexpr_fun name (){} \
}
#define GLOBAL_ARRAY_END(name) \
ARRAY_END_HELPER(name##_ele_); \
ARRAY_TYPE_CREATE(name##_ele_,name##_arrT) constexpr_obj name
/* * @def CLASS_ARRAY_BEGIN
* @brief 用於建立一個編譯期數組, 數組的元素可以在不同的地方聲明.
* CLASS_ARRAY_BEGIN 和 CLASS_ARRAY_END 用於在 class/struct 中定義數組的
* 元素, 其用法如下:
* @code
* // class definition in the .h file
* class Test{
* CLASS_ARRAY_BEGIN(int, arr);
* ARRAY_ELEMENT(arr, 1);
* ARRAY_ELEMENT(arr, 3);
* int class_member;
* ARRAY_ELEMENT(arr, 5);
*
* CLASS_ARRAY_END(arr);
* };
* // add this code in the .cpp file
* CLASS_ARRAY_IMPLEMENT(Test,arr);
* int main(){
* using namespace std;
* // Note that arr is a global variable,
* // not a member of struct Test
* cout<<"arr.length() = " <<arr.length()<<endl;
* for(int i=0; i<arr.length(); i++)
* cout<<"arr["<<i<<"] = "<<arr[i]<<endl;
* }
* // program output
* // arr.length() = 3
* // arr[0] = 1
* // arr[1] = 3
* // arr[2] = 5
* @endcode
* @note 雖然數組是在類/結構體內容定義的, 但是最終實現的數組
* 是一個全局數組, 而不是類的成員!
*/
#define CLASS_ARRAY_BEGIN GLOBAL_ARRAY_BEGIN
#define CLASS_ARRAY_END(name) \
ARRAY_END_HELPER(name##_ele_); \
friend struct name##_wrap
#define CLASS_ARRAY_IMPLEMENT(class_name, array) \
ARRAY_TYPE_CREATE(class_name::array##_ele_,array##_wrap) \
constexpr_obj array
#ifdef EMPTY_BASE_CLASS_OPTIMIZATION_ASSERT_FAILED
#undef ARRAY_TYPE_CREATE
#define ARRAY_TYPE_CREATE ARRAY_TYPE_CREATE_WITHOUT_EBCO
#endif
/* * empty base class optimization assert */
class EBCO_assert{
CLASS_ARRAY_BEGIN( int, array_1);
CLASS_ARRAY_BEGIN( char, array_2);
ARRAY_ELEMENT(array_1, 1);
ARRAY_ELEMENT(array_2, 2);
ARRAY_ELEMENT(array_1, 1);
ARRAY_ELEMENT(array_2, 2);
CLASS_ARRAY_END(array_1);
CLASS_ARRAY_END(array_2);
ARRAY_TYPE_CREATE(array_1_ele_,arr1_type);
ARRAY_TYPE_CREATE(array_2_ele_,arr2_type);
/* *< 如果這個 STATIC_ASSERT 失敗了, 可能是由於編譯器沒有做好空
基類優化. 可以嘗試在包含此頭文件之前添加宏定義
#define EMPTY_BASE_CLASS_OPTIMIZATION_ASSERT_FAILED
然后再重新編譯.
*/
STATIC_ASSERT( sizeof(arr1_type)== 2* sizeof( int)
&& sizeof(arr2_type)== 2* sizeof( char));
};
//////////////////////////////////////////////////////////// /
// 測試代碼
GLOBAL_ARRAY_BEGIN( int, arr);
ARRAY_ELEMENT(arr, 1);
ARRAY_ELEMENT(arr, 3);
int class_member;
ARRAY_ELEMENT(arr, 5);
GLOBAL_ARRAY_END(arr);
int main(){
using namespace std;
cout<< " arr.length() = " <<arr.length()<<endl;
for( int i= 0; i<arr.length(); i++)
cout<< " arr[ "<<i<< " ] = "<<arr[i]<<endl;
while( 1);
}
/* 程序輸出:
arr.length() = 3
arr[0] = 1
arr[1] = 3
arr[2] = 5
*/