C++编译期数据结构 - 在不同的地方声明全局数组的初始值
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
*/