為了做到能操作所有類型的數據,我參看了幾個類似的C語言的庫,基本上就是兩種做法:一是使用宏,二是使用void*指針。最后我選擇了后者,原因很簡單,我不是一個很推崇在代碼中大量使用宏的人,一方面覺得這樣作會讓代碼的可讀性降低,另一方面我也確實不是寫宏的高手。
在CGL中,有以下的幾個typedef都把void*定義為某種類型:
typedef void* container_t;
typedef void* point_t;
typedef void* data_t;
分別作一個解釋,container_t表示的是指向容器的指針,point_t表示的是通用指向某容器的指針,不論是指向數組成員的指針還是一個鏈表結點的指針都可以"泛化"的表示為"pos_t",而data_t表示的是存放數據的指針,之所以要對同樣可以表示為是void*的指針分三個類型的typedef,目的是為了在代碼中一目了然,看到類型的名字就能知道是作什么用的了。
container_t的含義很好理解,現在對后面兩種類型作一下解釋。
原本pos_t不叫pos_t的,而是被定義為iter_t,因為在STL中迭代器其實就是一個行為很像指針的東東,可以解引用,可以遞增指向下一個元素,遞減指向前一個元素,等等。但是需要注意的時候,由于C++中可以重載操作符,如*,++,--這樣的操作符都可以被重載以至于一個iterator的行為看上去和一個普通的指針沒有什么區別。但是在CGL中,是完全采用的C語言實現的,沒有辦法做到重載這些操作符,所以我專門提供了一個叫做iteraotr_t的結構體,里面有函數指針成員可以實現以上這些重載操作符所需要作的事情(后面會有專門的一節來講述這個結構體以及CGL中迭代器的設計),所以如果有一個類型為iter_t一個類型為iterator_t會不會讓人混淆呢?至少我偶爾回頭看我的代碼的時候是會弄混的,因此我決定把iter_t更名為pos_t也就是位置的意思。
data_t用于保存存放數據的指針,這里有幾個問題需要交待一下。首先是這樣作的弊端,雖然這樣避免前面提到的大量使用宏的缺點,但是卻浪費了存儲的空間以及會帶來一些使用上的不方便。先說浪費了存儲空間,以往存放一個數據只需要一個與該數據相同大小的空間就可以了,但是現在還需要多使用一個data_t指針指向分配好的空間,無形之中浪費了一個指針的空間。再說使用的不方便,以往處理數據的時候如果沒有特別的要求可以直接傳值,而現在必須傳指針,因為CGL的函數不認什么int,double,char類型,只處理指針。換句話說,假如f是CGL中的一個函數,如果要調用傳入一個整型參數5,你必須這樣作:
int nVal = 5;
f(&nVal);
而一般傳值就可以做到的調用是f(5)就可以了,這樣會造成使用上的不方便。
至于數據的賦值,我采用的C庫中memcpy函數,只要傳入指向數據的指針和數據的尺寸就可以,比較數據是否相等則采用C庫中的memcmp函數,所需要知道的參數和memcpy一樣,而當需要比較數據的大小時,這個比較頭疼,因為C庫中沒有根據指針和數據的大小進行比較的函數,我在后面會解釋我現在處理此類問題的辦法。
再來說說其他的兩個typedef:
typedef char* base_t;
typedef char bool_t;
最后的一個bool_t很好理解,就是一般的bool型嘛,之所以用char很簡單,char類型所需要的字節數最少,省空間。而base_t這個類型的含義是一個系統中最基本的數據類型,或者可以這么理解,別的數據類型所占有的字節數都可以表示為這個類型的算術操作,以上的言語也許晦澀了一些,我用例子來說明。
看CGL中一個函數的實現:
static point_t cgls_iter_advance(piterator_t pIter, size_t n)
{
base_t tTmp;

CGL_ASSERT(NULL != pIter);
CGL_ASSERT(0 <= n);

tTmp = (base_t)(pIter->tPoint);
pIter->tPoint = tTmp + pIter->nValSize * n;
return pIter->tPoint;
}

這個函數的作用是把迭代器pIter中保存的指向容器中數據的指針tPoint向前移動n個位置,大家知道指針的移動和它所指向的數據類型的大小有密切的關系,換句話說一個指針向前走n個位置所要移動的字節數為n * 它所指向的數據的尺寸,在上面的函數中,tPoint這個指針所指向的數據的尺寸存放在pIter的成員變量nValSize中,你也許會問直接使用sizeof(*tPoint)不就可以得到這個數值了么?別忘了我們前面說過所有的指針類型都是void*,而對void*指針是不能進行解引用操作的,所以我們需要一個變量來存放數據的尺寸。
注意到函數中的兩個操作:
tTmp = (base_t)(pIter->tPoint);
pIter->tPoint = tTmp + pIter->nValSize * n;
結合著base_t的定義,可以解釋為把void*指針tPoint強制轉化為char*,而tPoint向前走的位置為tTmp + nValSize*n,對于tTmp而言,它的類型是base_t也即是char*,sizeof(char) = 1,因此采用char*來保存以及進行指針的加減操作是最自然的操作,只要我們知道需要前進的步數(n),每部的幅度(nValSize),就可以通過把指針強制轉化為base_t也就是char*來達到我們所要到達的位置。
以上,是我對目前CGL中幾個typedef的解釋??梢钥吹降氖牵O計中總是存在著這樣那樣的折中,很多地方的處理也是不完美的,我選擇的是不向宏妥協而是自己對指針進行處理和操作。