在被 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 的必要性。