打算用C重新实现一个更完善的通讯协议描述语言,写的时候发现很多地方会需要用到可以自动增长的列表,于是尝试用宏定义的方式实现了一个简单的“泛型”列表。
用法很简单,主要的宏有两个,一个用于列表类型的定义,一个用于列表类型的实现,通常会在头文件里面定义列表类型在代码文件里实现它。
下面是声明一个整数列表的代码:
// 定义
LIST_DEF(int_list, int);
// 实现
LIST_IMP(int_list, int);
第一个宏参数 int_list 是列表类型名,int 是列表的元素类型,这里也可以换成自己的结构体。但是有时候我们会需要列表元素是指针类型,并且在销毁列表的时候要能自动回收每个项,所以另外有一个实现指针类型列表的宏,下面是声明一个字符串列表的代码:
// 定义
LIST_DEF(str_list, char *);
// 实现
LIST_IMP_P(str_list, char *, free);
这个宏比非指针版多了一个用于回收列表项的函数作为参数,回收字符串我们用 free 函数所以这里就传 free,有时候我们的自定义类型需要自己实现更复杂的 free,那就可以传自己实现的 free 进去。
LIST_DEF 展开会声明一个结构体和一组列表操作函数,列表操作函数有: add、del、get、new、free 五个基本操作,分别会以 [列表类型]_add、[列表类型]_del、[列表类型]_get、[列表类型]_new、[列表类型]_free 声明并实现,LIST_IMP 宏展开来就是这五个函数的实现代码。
下面是整数列表的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include "gen_list.h"
LIST_DEF(int_list, int);
LIST_IMP(int_list, int);
int main(int argc, char **argv) {
int i;
int pos;
int_list *list = int_list_new(1);
printf("cap = %02d, len = %02d\n", list->cap, list->len);
printf("*** add test ***\n");
for (i = 0; i < 20; i ++) {
pos = int_list_add(list, i);
printf(
"%02d - cap = %02d, len = %02d, list[%02d] = %d\n",
i, list->cap, list->len, pos, int_list_get(list, pos)
);
}
printf("*** del test ***\n");
for (i = 0; i < 20; i ++) {
int_list_del(list, 0);
printf(
"%02d - cap = %02d, len = %02d, list[%02d] = %d\n",
i, list->cap, list->len, 0, int_list_get(list, 0)
);
}
int_list_free(list);
return 0;
}
下面是 gen_list.h 的完整代码:
#ifndef _GEN_LIST_H_
#define _GEN_LIST_H_
#define LIST_DEF(LIST, T) \
typedef struct { \
T *items; \
int cap; \
int len; \
} LIST; \
\
LIST *LIST##_new(int cap); \
\
void LIST##_free(LIST *list); \
\
int LIST##_add(LIST *list, T item); \
\
void LIST##_del(LIST *list, int pos); \
\
T LIST##_get(LIST *list, int pos); \
#define LIST_IMP_(LIST, T, ALLOC, REALLOC, FREE, ERROR, FREE_ITEMS, TFREE) \
LIST *LIST##_new(int cap) { \
LIST *list = (LIST *)(ALLOC)(sizeof(LIST)); \
list->items = (T *)(ALLOC)(sizeof(T *) * cap); \
list->cap = cap; \
list->len = 0; \
return list; \
} \
\
void LIST##_free(LIST *list) { \
FREE_ITEMS \
(FREE)(list->items); \
(FREE)(list); \
} \
\
int LIST##_add(LIST *list, T item) { \
if (list->len == list->cap) { \
int new_cap = list->cap * 2; \
list->items = (T *)(REALLOC)(list->items, sizeof(T) * new_cap); \
if (list->items == NULL) { \
(ERROR)(#LIST" realloc failed!\n"); \
exit(1); \
} \
list->cap = new_cap; \
} \
int pos = list->len; \
list->items[pos] = item; \
list->len += 1; \
return pos; \
} \
\
void LIST##_del(LIST *list, int pos) { \
if (pos >= list->len) \
return; \
\
TFREE(list->items[pos]); \
\
int i; \
\
for (i = pos + 1; i < list->len; i ++) { \
list->items[i - 1] = list->items[i]; \
} \
\
list->len -= 1; \
} \
\
T LIST##_get(LIST *list, int pos) { \
if (pos >= list->len) \
return 0; \
\
return list->items[pos]; \
} \
#define LIST_FREE_ITEMS(TFREE) \
int i = 0; \
for(i = 0; i < list->len; i ++) { \
TFREE(list->items[i]); \
} \
#define LIST_IMP(LIST, T) \
LIST_IMP_( \
LIST, T, malloc, realloc, free, printf, , \
) \
#define LIST_IMP_P(LIST, T, TFREE) \
LIST_IMP_( \
LIST, T, malloc, realloc, free, printf, \
LIST_FREE_ITEMS(TFREE), TFREE \
) \
#endif
从代码可以看出来对于更复杂的自定义方式也是支持的,必要的时候可以用自己的函数替换 malloc、realloc、free、printf :)
原标签已从 2023-12-16 存档快照 恢复。本文按原站快照恢复排版,并接回已核实的本地图片。查看恢复说明