C言語でもdeferを使いたい #2
Table of Contents
c言語でdeferを実装してみよう。
まず、前回示したようにdeferを実装する構造体は次のようになる。
#include <stddef.h>
typedef struct defer_item{
void (*deleter)(void* context);
void *context;
}defer_item_t;
typedef struct defer_impl{
size_t len,cap;
defer_item_t *items;
}defer_impl_t;
そして、初期化・解消処理は次のように書ける。
#include <stdlib.h>
#include <assert.h>
// コンパイルオプション(gccなら-DDEFER_DEFAULT_CAP=XX)で
// 変更できるようにマクロを定義する。
#ifndef DEFER_DEFAULT_CAP
#define DEFER_DEFAULT_CAP 16
#endif/*DEFER_DEFAULT_CAP*/
defer_handle defer_open(){
defer_impl_t*const impl = malloc(sizeof(defer_impl_t));
defer_item_t*const items = malloc(DEFER_DEFAULT_CAP*sizeof(defer_item_t));
if (!impl||!items){
//early returnですぐに制御を戻す
free(impl);
free(items);
return NULL;
}
// implに設定を書き込みんでいく
impl->cap=DEFER_DEFAULT_CAP;
impl->len=0;
impl->items=items;//move ownership into impl
return impl;
}
void defer_close(defer_handle d){
assert(d);//d==NULLは明らかなコーディングミスなので落とす
defer_item_t *const rbegin=&d->items[d->len-1];
defer_item_t *const rend=&d->items[-1];
for (defer_item_t* iter=rbegin;iter!=rend;iter--){
iter->deleter(iter->context);
}
free(d->items);
free(d);
}
次に開放処理を登録する関数を示す。
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
int defer_push(defer_handle d, void(*deleter)(void*),void* context){
assert(d);
assert(deleter);
if (d->len+1>=d->cap){
// need resize
const size_t next_cap = 2*d->cap;
const defer_item_t* next_items=
realloc(d->item, d->next_cap*sizeof(next_items));
if (!next_item){
return ENOMEM;//通常の環境では発生しないはず
}
d->items=next_items;
d->cap=next_cap;
}
defer_item_t*const item= d->items[d->len++];
item->deleter=deleter;
item->context=context;
return 0;
}
最後にあまり使わないと思われる関数を示す。
void defer_pop(defer_handle d){
assert(d);
// ignore empty case
if (d->len==0){
return 0;
}
defer_item_t*const item= d->items[--d->len];
item->deleter(item->context);
}
size_t defer_depth(defer_handle d){
assert(d);
// static inlineにすると内部構造を隠蔽できない!!
return d->len;
}
void defer_pop_until(defer_handle d,size_t depth){
assert(d);
defer_item_t *const rbegin=&d->items[d->len-1];
defer_item_t *const rend=&d->items[depth-1];
for (defer_item_t* iter=rbegin;iter!=rend;iter--){
iter->deleter(iter->context);
}
d->len=depth;
}
では、楽しいC言語ライフを!!