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言語ライフを!!