c语言RAII实现
Intro
RAII是c++的看手好戏,用过的都说好。面对c,可没有RAII这种东西使用。还好,gcc提供了一些折中的方案来剑走偏峰,来实现RAII。如下:
__attribute__((cleanup(free-function)))
attribute的使用简介还请看ref。此不在讲解范围内。
具体用法
用这个实现自动回收str和tree;
/* Char Str free */
#define auto_sfree __attribute__ ((cleanup(sfree)))
#define auto_treefree __attribute__ ((cleanup(treefree)))
__attribute__((always_inline))
inline void sfree(void* str) {
if( !str ) return;
free( *(void**)str );
}
/* tree free */
__attribute__((always_inline))
inline void treefree(void* tree) {
if( !tree ) return;
tree_free_por( *(treenode**)tree );
}
usage:
int main(void) {
auto_sfree tmp_str = calloc(100,1);
}
Smart Pointer in C
我们可以使用这个编译器提供的特性来实现一个c的smart pointer。
|------------------------|---------------------- // -----|
| metadata | padding? | actual data |
|------------------------|---------------------- // -----|
^ ^ returned address (word aligned)
`- start of allocated
block
/*
*
* 储存数据和dtor的函数指针
*/
struct meta_data {
void (*dtor)(void*);
void* data;
};
// 从data的位置往前移动到struct meta_data的开始位置
static struct meta_data* get_meta(void* ptr) {
return ptr - sizeof(struct meta_data);
}
__attribute__((malloc))
void* smart_malloc(size_t size, void(*dtor)(void*)) {
// 一次性分配好struct meta_data的大小和需要分配的大小
struct meta_data* mdata = malloc(sizeof(struct meta_date) + size);
// 初始化
*mdata = (struct meta_data) {
.dtor = dtor,
.data = mdata + 1 // 加1实际上是 1 = sizeof( struct meta_data )
};
return mdata->data;
}
void smart_free(void* data) {
if( data == NULL ) return;
data = *(void**)data;
if( data == NULL ) return;
struct meta_data* mdata = get_meta(data);
assert( data == mdata->data);
if( mdata->dtor ) {
mdata->dtor( data );
}
free(mdata);
}
// usage
#define auto_free __attribute__ ((cleanup(smart_free)))
void test_1( ) {
// 当退出作用域之后会自动调用 smart_free(pint);
auto_free int* pint = smart_malloc(sizeof(int), NULL);
// 当然如果你的struct 之中是有ptr指向另外的地址需要释放,需要你自己定义destructor
// 如果你理解C++ RAII这些概念应该非常好理解
// malloc分配时,其原理也大致和这个差不多。
}
Advance Technique
从linux/list.h的container_of()看从get_meta()
getmeta的局限性是在抽象不够,只能获取metastruct的位置,而如果换个目标类型则不行。如果我们需要从struct member ptr 中获取我们指定类型的ptr返回地址。很明显这种写法不行。而Containerof则提供这样的能力。原本这是为了配套linux kernel定义的list实现,其基本思路还是和getmeta一样,只是containerof的设计更通用。详情可见container_of。
struct mystruct {
void* data;
struct list_head mylist;
};
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );})
tring的一种实现

这种实现可以保证string对象和原始char* str大小一样。

浙公网安备 33010602011771号