VSF构架中,各个模块都是使用类的方式来实现的,最近做了一个queue的类,就拿这个作为例子,演示一下C语言中,面向对象的实现。
队列的模型很简单,就是一个链表,只是我增加了每个节点的权重,用于排序。
这里就以TCPIP的使用环境作为例子:
1. 高层是多任务的,可能会在短时间内,把多个需要发送的IP报文推给底层驱动。但是,底层的硬件只能一个一个把数据写到wifi芯片,那么这里就会需要一个缓冲队列,并且,新来的发送报文,添加到队列的尾部
2. TCP的接收窗口管理,TCP协议在接收数据的时候,在定义的接收窗口范围内的报文,都可以接收,但是,接收到的报文可能是无序的。那么接收的报文也是放到一个缓冲队列,但是,这里多了一个排序,根据报文的sequence来决定报文的位置。
当然,其他地方都可以用到,比如定时器管理模块,所有的定时器也都是链表,这里也是使用这个队列来实现排序的,最近要触发的定时器排在最前面。
在面向对象的方式中,以前说过,是由类和接口实现的,在C语言中,类就是一个结构,接口就是一个函数(或者叫类函数)。
类函数的一个特诊,就是第一个参数是类的实例指针,就是很多面向对象语言中说的this指针。
C++在定义类函数的时候,不需要显式的定义this参数,但是编译器会自动加上。那么C语言的话,那就只能显式的定义这个参数了。
// queue
struct vsfq_node_t
{
uint32_t addr;
struct vsfq_node_t *next;
};
struct vsfq_t
{
struct vsfq_node_t *head;
struct vsfq_node_t *tail;
};
void vsfq_init(struct vsfq_t *q);
void vsfq_append(struct vsfq_t *q, struct vsfq_node_t *n);
void vsfq_remove(struct vsfq_t *q, struct vsfq_node_t *n);
void vsfq_enqueue(struct vsfq_t *q, struct vsfq_node_t *n);
struct vsfq_node_t* vsfq_dequeue(struct vsfq_t *q);
这个就是队列的定义,以及类函数接口。
append为了适应上述第一种情况,把新的node加入到尾部。
enqueue则是根据node里的地址,插入到适合位置。
remove是用队列里移出指定的node,而dequeue则是按照顺序,拿出头部的node
功能知道的话,代码其实也就是那几行而已。
比如,vsfq_init:
void vsfq_init(struct vsfq_t *q)
{
q->head = q->tail = NULL;
}
当然,这里的问题是,没有对指针做有效性检测。我们先忽略这些问题。
这里定义好queue的类后,就可以在应用中,声明的类直接集成自这个queue类就行。
C语言中,继承的2中方法:
1. 使用没有命名的结构体
struct vsf_buffer_queue_t
{
uint32_t addr;
struct vsf_buffer_queue_t *next;
uint8_t *buffer;
};
以上这个结构体,前面的部分完全兼容vsfq_node_t,直接可以使用vsfq_xxx的类函数来操作。
访问addr和next的方式,就是vsfq_node_t完全一样:var_name.addr和var_name.next
当然,一般建议这么写:
struct vsf_buffer_queue_t
{
struct
{
uint32_t addr;
struct vsf_buffer_queue_t *next;
};
uint8_t *buffer;
};
这里的结构没有名字,所以访问的方式一样:var_name.addr和var_name.next
当然,这个需要知道父类的内部结构,才可能这么实现。
2. 使用命令的结构体
struct vsf_buffer_queue_t
{
struct vsfq_node_t qnode;
uint8_t *buffer;
};
这种方式是不是简单一些,一般单一继承都可以用这种方法,直接可以吧vsf_buffer_queue_t结构的指针,转换成vsfq_node_t结构的指针。
编译结果上不损失任何效率,只是在使用的时候,需要经过强制类型转换。
访问方式上也需要通过一个结构内的变量来访问:var_name.qnode.addr和var_name.qnode.next,当然,这个也只是影响C代码,不影响编译后的效率。
另外,代码没经过验证,有问题请自行脑补。
|