OO Programing in C is not only
POSSIBLE, but also PRACTICAL.
--------------------------------------------------------------------------------
“class“是很多OO编程语言里的关键字,它来源于OO鼻祖Smalltalk。class(类),是对一群有相同特性的对象的抽象概括,对象称为类的实例。在class里面可以存放有状态(变量),行为(函数/方法)....有关OO概念、方法的文章太多了,不再啰嗦。在C里面,唯一可以实现自定义类型的是struct,struct是C的OO编程最重要的工具。
一个最常见的技巧,就是用struct来"仿真"class: 在struct里面放入变量,函数指针,嵌入其他struct等。
以下例子摘自我最近刚开发完成的一个USB Firmware项目:
C代码
structusb_device;
structusb_ctl;
structusb_iobuf {
intlen;
unsignedcharbuf[USBEPFIFO_SIZE];
};
structusb_endpoint {inttype;
intqlen;
xQueueHandle lock;
xQueueHandle q;
intidx;
intepx;
intcfg;
intbank;
inttxCount;void(*ep_process) (structusb_device *dev,structusb_endpoint *ep, xISRStatus *pxMessage);
};
structusb_descriptor {
inttype;
intidx;
intsize;
void* data;
structlist_head list;
};
structusb_deviceOps {
int(*init)(structusb_device *dev);
int(*reset)(structusb_device *dev);
int(*switch_in)(structusb_device *dev);
int(*switch_out)(structusb_device *dev);
void(*class_interface_req)(structusb_device *dev, xUSB_REQUEST *pxRequest);int(*ctl_data_comp)(structusb_device *dev, xCONTROL_MESSAGE *pxMessage);
};
structusb_ctlOps {
void(*ctl_transmit_null)(structusb_ctl *ctl);
void(*ctl_send_stall)(structusb_ctl *ctl);
void(*ctl_reset_ep0)(structusb_ctl *ctl);
void(*ctl_detach_usb)(structusb_ctl *ctl);
void(*ctl_attach_usb)(structusb_ctl *ctl);
void(*ctl_send_data)(structusb_ctl *ctl, unsignedchar*data,
intreq_len,
intsend_len,
intis_des);
};
structusb_ctl {
intaddr;
intconf;
eDRIVER_STATE state;
xCONTROL_MESSAGE tx;
xCONTROL_MESSAGE rx;
structubufm *bufmn;
intprio;
xTaskHandle task_handle;
structusb_ctlOps *ctlOps;
};
structusb_device {
charname[16];
structusb_deviceOps *ops;
structusb_ctl *ctl;
structlist_head desc_list;
structusb_endpoint *ep[MAX_ENDPOINTS];
intactive;
xQueueHandle ready;
void*private;
structlist_head list;
};
struct usb_device;
struct usb_ctl;
struct usb_iobuf {
int len;
unsigned char buf[USBEPFIFO_SIZE];
};
struct usb_endpoint { int type;
int qlen;
xQueueHandle lock;
xQueueHandle q;
int idx;
int epx;
int cfg;
int bank;
int txCount; void (*ep_process) (struct usb_device *dev, struct usb_endpoint *ep, xISRStatus *pxMessage);
};
struct usb_descriptor {
int type;
int idx;
int size;
void * data;
struct list_head list;
};
struct usb_deviceOps {
int (*init)(struct usb_device *dev);
int (*reset)(struct usb_device *dev);
int (*switch_in)(struct usb_device *dev);
int (*switch_out)(struct usb_device *dev);
void (*class_interface_req)(struct usb_device *dev, xUSB_REQUEST *pxRequest); int (*ctl_data_comp)(struct usb_device *dev, xCONTROL_MESSAGE *pxMessage);
};
struct usb_ctlOps {
void (*ctl_transmit_null)(struct usb_ctl *ctl);
void (*ctl_send_stall)(struct usb_ctl *ctl);
void (*ctl_reset_ep0)(struct usb_ctl *ctl);
void (*ctl_detach_usb)(struct usb_ctl *ctl);
void (*ctl_attach_usb)(struct usb_ctl *ctl);
void (*ctl_send_data)(struct usb_ctl *ctl, unsigned char *data,
int req_len,
int send_len,
int is_des);
};
struct usb_ctl {
int addr;
int conf;
eDRIVER_STATE state;
xCONTROL_MESSAGE tx;
xCONTROL_MESSAGE rx;
struct ubufm *bufmn;
int prio;
xTaskHandle task_handle;
struct usb_ctlOps *ctlOps;
};
struct usb_device {
char name[16];
struct usb_deviceOps *ops;
struct usb_ctl *ctl;
struct list_head desc_list;
struct usb_endpoint *ep[MAX_ENDPOINTS];
int active;
xQueueHandle ready;
void *private;
struct list_head list;
};
在这个例子,我用struct分别描述了USB设备,USB控制通道,USB端点,USB描述符和USB缓冲区对象。USB设备对象包含了若干个USB端点,一个USB控制通道指针,一个USB描述符表的表头(指向若干个USB描述符),和一个USB缓冲区管理对象。而且,USB设备对象还包含了name属性,一个由USB
Framework调用的回调函数集,还有一个用于连接其他USB设备的链表节点。
值得一提的是,USB设备对象中有一个void *private
成员,可以指向任何数据。实际上在我的程序里,我实现了usb-serial和usb-mass-storage两个USB设备,对于usb-serial对象,private我弃之不用,而在usb-mass-storage对象中,private指向一个Mass
storage对象,usb-mass-storage正是通过这个Mass
storage对象访问外部大容量存储的(在我的程序里,Mass storage对象和一个MMC
Card对象绑定,外部存储是SD/MMC卡)。由于对于每一种设备的具体实现来说,它知道private指向的是何种类型的设备,因此不会引起混乱。而外部程序根据需要在初始化USB设备对象前赋予private有意义的值——运行时动态绑定。
这一系列struct基本上如实地反映了USB DEVICE硬件逻辑和规范要求:
"一个USB设备包含若干个端点,其中有一个固定的控制端点(端点0)。在枚举阶段USB设备要根据HOST的请求应答相应的描述符..."
现在回到OO的话题,这个例子中体现了"组合类":USB设备对象包含了USB端点对象,USB描述符对象...。还有动态绑定
(private成员)。从严格的OO意义上来看,好像有点"怪",不过我认为这恰恰是C的特点——简洁,直接。不信你用C++表达试试?也许会更漂亮,很OO,但是不一定会如此清爽!
P.S. : 熟悉USB Firmware开发的人可能对struct
usb_endpoint中的epx,cfg,bank和txCount四个成员有异议,因为这些成员是和特定的硬件相关,并不是所有的USB硬件都支持ping-pong
mode,所以bank和txCount不一定用得上,epx,
cfg也可能因硬件的不同而不同。没错!更理想的设计是把与硬件相关的部分分离出来,用void
*private指向各自的与硬件相关的配置——就像struct usb_device所采用方法,所以更好的版本应该是:
Java代码
struct usb_endpoint {
inttype;
intqlen;
xQueueHandle lock;
xQueueHandle q;
intidx;
void(*ep_process)(struct usb_device *dev, struct usb_endpoint *ep, xISRStatus *pxMessage);
void*private;
};
struct usb_endpoint {
int type;
int qlen;
xQueueHandle lock;
xQueueHandle q;
int idx;
void (*ep_process)(struct usb_device *dev, struct usb_endpoint *ep, xISRStatus *pxMessage);
void *private;
};
tips: 用C表达的一个关键处就是要很好地应用struct来描述模型。