Cで型安全可変長配列ライブラリをつくる
Cで汎用ライブラリをつくってると、void*の嵐になって型安全が無くなると思う。
ちょっとした手間で、それをなんとかする話。
struct varray { int nelem; void *values; }; #define VSET( t, a, i, e ) (((t*)a##t.values)[i] = e) #define VDECL( t, name ) struct varray name##t
こんなふうにして。
int main() { VDECL( int, a ); VSET( int, a, 0, 4 ); }
こんなふうにする。
変数宣言がちょっとキモくなるのと、配列アクセスが、キモくなる。のと、配列アクセスするときに型の名前がいる。
のだけど、
int main() { VDECL( int, a ); VSET( float, a, 0, 4.0 ); }
こういうのをコンパイルエラーにできる。原理は…名前のあとに型の名前をつけてるだけという。(なので、若干気にしないといけない。型に括弧付けてはいけないとか)
$ cat varray.c struct varray { int nelem; void *values; }; #define VSET( t, a, i, e ) (((t*)a##t.values)[i] = e) #define VDECL( t, name ) struct varray name##t struct nanika { VDECL( int, a ); }; int main() { struct nanika n; VDECL( int, a ); VSET( int, a, 0, 4 ); VSET( int, n.a, 0, 4 ); } $ gcc -E varray.c # 1 "varray.c" # 1 "<built-in>" # 1 "<コマンドライン>" # 1 "varray.c" struct varray { int nelem; void *values; }; struct nanika { struct varray aint; }; int main() { struct nanika n; struct varray aint; (((int*)aint.values)[0] = 4); (((int*)n.aint.values)[0] = 4); }
struct とかポインタとかはtypedefして。
オリジナルは…どっかで見たような気もするし、見てないような気もする。ちょっと思い出せない。