Cは低レベル言語であり、ほぼ移植可能なアセンブラーであるため、そのデータ構造と言語構造は金属に近い(データ構造には、ハードウェアとABIによって課されるパディング、配置、サイズの制約を除いて、隠れたコストはありません)。そのため、Cには、ネイティブの動的型付けはありません。ただし、必要な場合は、すべての値がいくつかの型情報(例:...)で始まる集約であるという規則を採用できます。使用-s及び(アレイのようなもののための)フレキシブル配列メンバに、アレイのサイズを含みます。enum
union
struct
(Cでプログラミングする場合、特に事前条件と事後条件および不変条件を定義、文書化、および従うのはユーザーの責任です。また、C動的メモリ割り当てではfree
、ヒープmalloc
メモリーゾーンをだれにするかについて明示的な規則が必要です)
したがって、ボックス化された整数、文字列、または一種のSchemeのような記号、または値のベクトルである値を表すには、概念的には、タグ付きユニオン(ポインターのユニオンとして実装)を使用します。-例:
enum value_kind_en {V_NONE, V_INT, V_STRING, V_SYMBOL, V_VECTOR};
union value_en { // this union takes a word in memory
const void* vptr; // generic pointer, e.g. to free it
enum value_kind_en* vkind; // the value of *vkind decides which member to use
struct intvalue_st* vint;
struct strvalue_st* vstr;
struct symbvalue_st* vsymb;
struct vectvalue_st* vvect;
};
typedef union value_en value_t;
#define NULL_VALUE ((value_t){NULL})
struct intvalue_st {
enum value_kind_en kind; // always V_INT for intvalue_st
int num;
};
struct strvalue_st {
enum value_kind_en kind; // always V_STRING for strvalue_st
const char*str;
};
struct symbvalue_st {
enum value_kind_en kind; // V_SYMBOL
struct strvalue_st* symbname;
value_t symbvalue;
};
struct vectvalue_st {
enum value_kind_en kind; // V_VECTOR;
unsigned veclength;
value_t veccomp[]; // flexible array of veclength components.
};
動的な型の値を取得するには
enum value_kind_en value_type(value_t v) {
if (v.vptr != NULL) return *(v.vkind);
else return V_NONE;
}
以下は、ベクトルへの「動的キャスト」です。
struct vectvalue_st* dyncast_vector (value_t v) {
if (value_type(v) == V_VECTOR) return v->vvect;
else return NULL;
}
ベクトル内の「安全なアクセサー」:
value_t vector_nth(value_t v, unsigned rk) {
struct vectvalue_st* vecp = dyncast_vector(v);
if (vecp && rk < vecp->veclength) return vecp->veccomp[rk];
else return NULL_VALUE;
}
通常は、上記の短い関数のほとんどをstatic inline
ヘッダーファイルのように定義します。
ところで、Boehmのガベージコレクターを使用できる場合は、いくつかの高レベル(ただし安全ではない)スタイルで非常に簡単にコーディングでき、いくつかのSchemeインタープリターがそのように行われます。可変個ベクトルコンストラクターは
value_t make_vector(unsigned size, ... /*value_t arguments*/) {
struct vectvalue_st* vec = GC_MALLOC(sizeof(*vec)+size*sizeof(value));
vec->kind = V_VECTOR;
va_args args;
va_start (args, size);
for (unsigned ix=0; ix<size; ix++)
vec->veccomp[ix] = va_arg(args,value_t);
va_end (args);
return (value_t){vec};
}
3つの変数がある場合
value_t v1 = somevalue(), v2 = otherval(), v3 = NULL_VALUE;
あなたはそれらを使用してベクトルを構築することができます make_vector(3,v1,v2,v3)
Boehmのガベージコレクターを使用したくない場合(または独自のガベージコレクターを設計したくない場合)は、デストラクタを定義し、誰が、どのように、いつメモリをfree
-dにするかをドキュメント化する際に十分注意する必要があります。この例を参照してください。したがってmalloc
、GC_MALLOC
上記の代わりに(ただし、その失敗に対してテストする)を使用できますが、デストラクタ関数を慎重に定義して使用する必要がありますvoid destroy_value(value_t)
Cの長所は、上記のようなコードを可能にし、独自の規則(特にソフトウェア)を定義するのに十分な低レベルであることです。