A Generic vector implementation in C

时间:2021-09-25 16:12:43

在被 linux 的 include/list.h 打击到彻底失去信心之前赶快贴过来,立此存照。

#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

struct _elem_type_tag
{
  size_t _type_size;
  int (*equals) (void *v1, void *v2, struct _elem_type_tag * tag);
  int (*greater) (void *v1, void *v2, struct _elem_type_tag * tag);
  void (*assign) (void *v1, void *v2, struct _elem_type_tag * tag);
};

int
default_equals (void *v1, void *v2, struct _elem_type_tag *tag)
{
  return memcmp (v1, v2, tag->_type_size) == 0;
}

int
default_greater (void *v1, void *v2, struct _elem_type_tag *tag)
{
  return memcmp (v1, v2, tag->_type_size) > 0;
}

void
default_assign (void *v1, void *v2, struct _elem_type_tag *tag)
{
  memcpy (v1, v2, tag->_type_size);
}

#define vt(type) struct vector_##type

struct vector_op
{
  int (*push_back) (void *p, void *vp /* vt(type) * */ );
  int (*pop_back) (void *p, void *vp);

  int (*get_size) (int *sz, void *vp);
};

struct default_vector
{
  void *baseaddr;
  size_t bufsz;
  size_t elemcount;

  struct _elem_type_tag *ttag;
  struct vector_op *vop;
};

int
default_push_back (void *p, void *vp)
{
  struct default_vector *dv = (struct default_vector *) vp;
  struct _elem_type_tag *ttag = dv->ttag;
  unsigned char *baddr = dv->baseaddr;
  size_t esz = ttag->_type_size;

  if (dv->elemcount * esz == dv->bufsz)
    {
      /* insufficient buffer */
      unsigned nelem = dv->elemcount * 1.5;
      nelem = nelem == 0 ? 10 : nelem;
      unsigned char *nbuf = calloc (nelem, ttag->_type_size);
      if (!nbuf)
        return 0;
      memmove (nbuf, dv->baseaddr, dv->bufsz);
      dv->ttag->assign (nbuf + dv->bufsz, p, ttag);
      dv->baseaddr = nbuf;
      dv->bufsz = nelem * ttag->_type_size;
    }
  else
    {
      dv->ttag->assign (baddr + dv->elemcount * ttag->_type_size, p, ttag);
    }
  dv->elemcount += 1;
  return 1;
}

int
default_pop_back (void *p, void *vp)
{
  struct default_vector *dv = (struct default_vector *) vp;
  struct _elem_type_tag *ttag = dv->ttag;
  unsigned char *baddr = dv->baseaddr;

  if (dv->elemcount > 0)
    {
      ttag->assign (p, baddr + (dv->elemcount - 1) * ttag->_type_size, ttag);
      dv->elemcount -= 1;
      return 1;
    }
  return 0;
}

int
default_get_size (int *sz, void *vp)
{
  struct default_vector *dv = (struct default_vector *) vp;
  if (!sz)
    return 0;
  *sz = (int) dv->elemcount;
  return 1;
}

#define def_vt(type) struct vector_##type { /
  void *baseaddr; /
  size_t bufsz; /
  size_t elemcount; /
/
  struct _elem_type_tag *ttag; /
  struct vector_op *vop; /
}

#define define_vt(type,traits,tvop) /
  { /
    .baseaddr = NULL, /
    .elemcount = 0, /
    .ttag = &traits, /
    .vop = &tvop, /
  }

def_vt (int);
def_vt (double);

struct _elem_type_tag default_ttag = {
  .equals = default_equals,
  .greater = default_greater,
  .assign = default_assign,
};

struct vector_op default_vop = {
  .push_back = default_push_back,
  .pop_back = default_pop_back,
  .get_size = default_get_size,
};

int
main ()
{
  double dt;
  int it;
  int iv = 1;

  vt (int) vint = define_vt (int, default_ttag, default_vop);
  vint.ttag->_type_size = sizeof (int);
  vt (double) vd = define_vt (double, default_ttag, default_vop);
  vd.ttag->_type_size = sizeof (double);

  vint.vop->push_back (&iv, &vint);
  vint.vop->pop_back (&it, &vint);
  printf ("Value poped: %d/n", it);

  dt = 2.0;
  vd.vop->push_back (&dt, &vd);
  vd.vop->pop_back (&dt, &vd);
  printf ("Value poped: %e/n", dt);

  return 0;
}

基本达到最初步的要求,可以在同一个编译单元内混和使用多种类型“实例化”的“模板类型”了。default_* 是利用 type size 做的假泛型。要真泛型的,自己定义 struct _elem_type_tag 和 struct vector_op 就可以。这里 _elem_type_tag 就相当于 STL 的 type traits 的作用。实际上,我的代码中 vector_op 使用了 template method 模式,调用 _elem_type_tag 里的赋值方法,应该基本少有重新定义 vector_op 的必要性。