我正在编写一种动态类型的语言。目前,我的对象以这种方式表示:
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; };
目标是我应该能够将所有内容作为 a 传递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;
};
这会产生一定程度的重定向,从而减慢速度。速度是这里的目标。
最后一种选择是传递void*
s 并自己管理结构的内部。例如,要实现上面提到的类型测试:
void* foo = bar();
// increment foo
if(*((struct Class*) foo) == integer)
(*((int*)(foo + sizeof(struct Class*))))++;
else
handleTypeError();
这给了我想要的一切(便携性、不同类型的不同尺寸等),但至少有两个缺点:
- 丑陋,容易出错 C. 上面的代码只计算了单个成员的偏移量;对于比整数更复杂的类型,情况会变得更糟。我也许可以使用宏来缓解这个问题,但无论如何这都会很痛苦。
- 由于没有
struct
代表对象,我没有堆栈分配的选项(至少没有在堆上实现我自己的堆栈)。
基本上,我的问题是,我怎样才能在不付钱的情况下得到我想要的东西?有没有一种方法可以移植,不同类型的大小有差异,不使用重定向,并保持我的代码漂亮?
编辑:这是我收到的关于 SO 问题的最佳回复。选择答案很困难。所以只允许我选择一个答案,所以我选择了一个能引导我找到解决方案的答案,但你们都收到了赞成票。