那谁的技术博客

感兴趣领域:高性能服务器编程,存储,算法,Linux内核
随笔 - 210, 文章 - 0, 评论 - 1183, 引用 - 0
数据加载中……

CGL开发手记之二--CGL中的数据类型

为了做到能操作所有类型的数据,我参看了几个类似的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的解释。可以看到的是,设计中总是存在着这样那样的折中,很多地方的处理也是不完美的,我选择的是不向宏妥协而是自己对指针进行处理和操作。

posted on 2007-05-02 00:59 那谁 阅读(772) 评论(0)  编辑 收藏 引用 所属分类: CGL


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理