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して。


オリジナルは…どっかで見たような気もするし、見てないような気もする。ちょっと思い出せない。


あと、配列ライブラリというと、gccのvecとかもおすすめ。(gcc/vec.h)