如何在编译器中实现类

时间:2010-10-28 13:38:52

标签: compiler-construction language-design llvm

我想为我自己的小语言实现一个类类型,但我最初想的不会太难让我难过。我有解析器,这是我遇到问题的代码生成方面。任何人都可以对最佳/正确的方法有所了解吗?具体来说,我想在LLVM中这样做,所以当我需要知道这个任何特定LLVM代码的一般性时,我应该使用它会很棒。

谢谢你。


N.B。我对LLVM的体验基本上来自于Kaleidoscope教程,而且还有一些额外的内容,但我还远没有完全理解LLVM API。

3 个答案:

答案 0 :(得分:6)

一个非常非常不完整的概述:

是一个结构(你知道C / C ++不是吗?)

方法是普通函数,除非它们接收一个额外的隐式参数:对象本身。该参数在函数中通常称为“this”或“self”。默认情况下,类方法符号可以(C ++,JavaScript)或不可以(PHP,Python)在方法中访问。

继承实质上是将结构粘合在一起,也可能合并符号表,因为通常默认情况下可以从您正在解析的类的方法中访问基类的符号。当您在方法中遇到符号(字段或方法)时,您需要执行升序查找,从当前上一层的层次结构开始。或者您可以实现它,以便只在一个符号表中查找,这是合并的结果。

间接调用

虚拟方法。在某些语言中,所有方法默认都是虚拟的。实现将取决于它是否是一种完全动态的语言,在这种情况下,您总是在运行时中查找类中的函数名称,因此您的所有方法都会自动变为虚拟;或者在静态语言的情况下,编译器通常构建所谓的虚拟方法表。我不确定你是否需要这个,所以我不会在这里详细说明。

构造函数是在构造新对象时调用的特殊方法(通常使用'new'),或者从后代构造函数中作为构造函数调用链的一部分调用。这里有许多不同的实现,一个是构造函数采用隐式的'this'参数,如果尚未创建对象,则该参数可能为NULL,并返回它。

Destructiors 是普通方法,通常在对象超出范围时隐式调用。再次,您需要考虑析构函数的升序调用链的可能性。

接口非常棘手,除非您的语言完全是动态的。

答案 1 :(得分:4)

您应该购买Stan Lippmann, Inside C ++对象模型。您需要的一切都在那里。

答案 2 :(得分:1)

可能有几种策略可以实现这一点,这里有一个:

vtable (虚拟表)是一个带有函数指针的编译时常量结构。 (所有值在编译时都是已知的。)

(如果需要,可以将指针调用到vtable和“接口”。)

没有任何继承能力的语言中的OOP类是一个结构,它包含一个指向其vtable的const指针作为第一个成员变量。 该指针用于精确识别对象的类型,并使用多继承方式对该对象进行方面/视图(如何进行了转换?)。

如果要进行多重继承,则需要能够(static_)将指向派生类的指针强制转换为其父类,并动态更正字节地址。这可以通过一个虚函数来实现,或者(更好)使用存储在vtable中的带符号偏移值来实现。

从指向父类的指针到指向派生类的指针的(dynamic_)强制转换意味着在可能很大的数据结构(数组,散列表,无论什么)中进行查找,或者通过一个虚函数实现。

每次从vtable调用一个函数都需要将对象指针转换为适合该函数的类型。这可以由调用者完成,从vtable读取带符号的偏移量(对函数进行修改),或者由被调用者完成,然后被调用者只是原始函数的代理。

在某些语言(尤其是函数式语言)中,您可以定义对(无类型)对象的引用,这些对象会实例化该对象上有效的接口/类型类列表。这样的引用包含一个指向基础对象的指针和一个指向相关vtable的指针列表。