模板
使用宏实现模板
使用宏可以在编译期实现模板,它和c++等语言的模板本质一样,通过对不同的类型进行codegen来实例化代码,但是因为语言本身局限,不支持隐式实例化和特化。但是在实际开发中完全够用,可以复用大量代码,只是易用性略差。 和c++模板一样,它会导致代码膨胀。
#define DECL_ADD(type) \
void add_##type(type* dst, type* lhs, type* rhs, unsigned int num);
#define IMPL_ADD(type) \
void add_##type(type* dst, type* lhs, type* rhs, unsigned int num) { \
for (unsinged int i = 0; i < num; i++) { \
dst[i] = lhs[i] + rhs[i]; \
} \
}
#define CALL_ADD(type) add_##type
DECL_ADD(int);
DECL_ADD(float);
IMPL_ADD(int);
IMPL_ADD(float);
CALL_ADD(int)(dst, lhs, rhs, num);
CALL_ADD(float)(dst, lhs, rhs, num);
使用不透明指针实现模板
什么是不透明指针?
顾名思义,不透明是我们看不到的。例如,木材是不透明的。不透明指针是指向数据结构的指针,该数据结构的内容在定义之时不公开。 在泛型的实现里,我们使用void来实现不透明指针,因为它可以指向任何内存,而不用关心具体的类型,这正是泛型所需要的。 使用不透明指针来实现模板,几乎不会造成任何代码膨胀,这是它的优势,但是性能略差,因为它可能会本来在寄存器上完成的计算强行转移到栈上。*
typedef void (add_func_t*)(void*, void* , void* );
void add(void* dst, void* lhs, void* rhs, unsigned int num, unsigned int elem_size, add_func_t func_ptr) {
char* dst_p = (char*)dst;
char* lhs_p = (char*)lhs;
char* rhs_p = (char*)rhs;
for (unsigned int i = 0; i < num; i++) {
func_ptr(dst_p, lhs_p, rhs_p);
dst_p += elem_size;
lhs_p += elem_size;
rhs_p += elem_size;
}
}
void add_int(void* dst, void* lhs, void* rhs) {
*(int*)dst = *(int*)lhs + *(int*)rhs;
}
void add_float(void* dst, void* lhs, void* rhs) {
*(float*)dst = *(float*)lhs + *(float*)rhs;
}
add(dst, lhs, rhs, num, sizeof(int), add_int);
add(dst, lhs, rhs, num, sizeof(float), add_float);
侵入式容器
注意:侵入式容器并不是真正的模板,它只能复用带有链的数据结构的逻辑,诸如链表,树和图等 普通链表: 我们经常使用的普通链表是每个节点的next指针指向下一个节点的首地址:
struct node
{
int data;
struct node* next;
}
普通链表的缺点:
- 一条链表上的所有节点的数据类型需要完全一致
- 对某条链表的操作如插入,删除等只能对这种类型的链表进行操作,如果链表的类型换了,就要重新再封装出一套一样的操作,泛化能力差 侵入式链表: 侵入式链表的节点的链接成员指向的是下一个节点的链接成员:
节点结构如下:
typ**edef struct link
{
struct link* next;
}list_t;
typedef struct
{
int data;
struct link* list;
} node;
侵入式链表的优点:
- 节点类型无需一致,只需要包含list_t成员即可
- 泛化能力强,所有链表的操作方式均可统一; 访问侵入式链表中的节点数据,需要使用offsetof和container_of
typedef struct link
{
struct link* next;
struct link* prev;
} list_t;
#define LIST_HEAD_INIT(name) {&(name), &(name)}
#define LIST_HEAD(name) list_t name = LIST_HEAD_INIT(name)
#define list_entry(node, type, member) \
container_of(node, type, member)
// 下面这些api都是通用的,无论data是数据类型
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
void list_init(list_t* list);
void list_insert_after(list_t* list, list_t* node);
void list_insert_before(list_t* list, list_t* node);
void list_remove(list_t* node);
int list_isempty(const list_t* list);
unsigned int list_size(const list_t* list);
已知link的情况下,如何拿到data的值
struct node
{
int data;
list_t link;
}
list_t link_p;
int val = list_entry(link_p, struct node, link)->data;
以下实现都是通用的
#include "link_list.h"
void list_init(list_t* list)
{
list->next = list->prev = list;
}
void list_insert_after(list_t* list, list_t* node)
{
list->next->prev = node;
node->next = list->next;
list->next = node;
node->prev = list;
}
void list_insert_before(list_t* list, list_t* node)
{
list->prev->next = node;
node->prev = list->prev;
list->prev = node;
node->next = list;
}
void list_remove(list_t* node)
{
node->next->prev = node->prev;
node->prev->next = node->next;
node->next = node->prev = node;
}
int list_isempty(const list_t* list)
{
return list->next == list;
}
unsigned int list_size(const list_t* list)
{
unsigned int size = 0;
const list_t* p = list;
while (p->next != list)
{
p = p->next;
size++;
}
return size;
}