私は動的型付け言語を書いています。現在、私のオブジェクトは次のように表されています。
struct Class { struct Class* class; struct Object* (*get)(struct Object*,struct Object*); };
struct Integer { struct Class* class; int value; };
struct Object { struct Class* class; };
struct String { struct Class* class; size_t length; char* characters; };
目標は、すべてを として渡し、属性struct Object*
を比較してオブジェクトのタイプを発見できるようにすることです。class
たとえば、使用するために整数をキャストするには、次のようにします (integer
タイプが であると仮定しますstruct Class*
)。
struct Object* foo = bar();
// increment foo
if(foo->class == integer)
((struct Integer*)foo)->value++;
else
handleTypeError();
問題は、私が知る限り、C 標準では構造体の格納方法が保証されていないことです。私のプラットフォームでは、これは機能します。しかし、別のプラットフォームでは、前にstruct String
保存し、上記でアクセスしたときに、実際にはにアクセスしている可能性があり、これは明らかに悪いことです。ここでの移植性は大きな目標です。value
class
foo->class
foo->value
このアプローチの代替手段があります。
struct Object
{
struct Class* class;
union Value
{
struct Class c;
int i;
struct String s;
} value;
};
ここでの問題は、共用体に格納できる最大のもののサイズと同じくらいのスペースを共用体が使用することです。一部の型が他の型の何倍も大きいことを考えると、これは、小さな型 ( int
) が大きな型 ( ) と同じくらいのスペースを占有することを意味し、map
これは受け入れがたいトレードオフです。
struct Object
{
struct Class* class;
void* value;
};
これにより、動作が遅くなるレベルのリダイレクトが作成されます。ここでは速度が目標です。
最後の選択肢は、 s を渡しvoid*
、構造体の内部を自分で管理することです。たとえば、上記の型テストを実装するには:
void* foo = bar();
// increment foo
if(*((struct Class*) foo) == integer)
(*((int*)(foo + sizeof(struct Class*))))++;
else
handleTypeError();
これにより、私が望むすべて (移植性、異なるタイプの異なるサイズなど) が得られますが、少なくとも 2 つの欠点があります。
- 恐ろしい、エラーが発生しやすい C. 上記のコードは、単一メンバーのオフセットのみを計算します。整数よりも複雑な型ではさらに悪化します。マクロを使えば少しは軽減できるかもしれませんが、これはどう考えても痛いです。
- オブジェクトを表すものがないため
struct
、スタック割り当てのオプションはありません (少なくとも、ヒープに独自のスタックを実装する必要はありません)。
基本的に、私の質問は、どうすればお金を払わずに欲しいものを手に入れることができるでしょうか? 移植性があり、タイプごとにサイズが異なり、リダイレクトを使用せず、コードをきれいに保つ方法はありますか?
編集: これは、SO の質問に対して私が今まで受け取った中で最高の応答です。答えを選ぶのは難しかった。SO では 1 つの回答しか選択できないため、解決策につながる回答を選択しましたが、皆さんは賛成票を受け取りました。