我正在用C编写语言解释器,我目前正在实现一个系统,允许用C语言为解释器编写扩展模块。
这些模块像普通模块一样加载到代码文件中,但在幕后它们是用C编写的动态加载库。这是一种类似于Python解释器中的方法。
在C代码中(无论是在主解释器中还是在扩展模块中),当前实例化类的方法如下:
ObjectInstance* instance = vm_instantiate_class(class_object, arguments);vm_instantiate_class负责分配新实例,如果存在则调用其类的初始化器方法,以及一些更多的簿记。
ObjectInstance* vm_instantiate_class(ObjectClass* klass, ValueArray args) {
// Allocate instance
ObjectInstance* instance = object_instance_new(klass);
// This goes to the instance's class, gets the requested attribute, and if it's a method,
// the method is wrapped with a BoundMethod so it remembers it's associated instance when it's called
ObjectBoundMethod* init_method = (ObjectBoundMethod*) object_load_attribute((Object*) instance, "@init");
// Invoke the initializer on the instance
vm_call_bound_method(init_method, args);
instance->is_initialized = true;
return instance;
}这对于创建实例非常有用,无论是在主解释器循环中调用用户代码时,还是从C扩展调用它。
但是,在考虑如何实例化本机类时,我陷入了两难境地。
当前,扩展中的本机类实现如下所示。我们定义了一个“继承”ObjectInstance的结构:
typedef struct {
ObjectInstance base;
// ... other native fields
int x;
int y;
} ObjectInstanceMyClass;如上面所示,当object_instance_new被vm_instantiate_class调用时,它会检查类对象是本机类还是用户类。如果是本机内存,类对象将说明实际需要为实例分配多少内存(例如sizeof(ObjectInstanceMyClass))。如果是用户类,则分配sizeof(ObjectInstance)。
这允许用户代码可以看到本机实例,就像任何其他实例一样--但是它实际上可以在内部携带本机数据,在通过语言公开的数据类型之外。
在我正在编写的扩展中,有一个本机类包装了本机OS资源--假设有一个文件句柄。在扩展本身中,我希望能够像实例化常规类一样优雅地实例化它:
ValueArray args = make_value_array();
value_array_write(args, <a native C file handle>);
ObjectInstanceFile* file_object = (ObjectInstanceFile*) vm_instantiate_class(file_class, args);但是,我不能这样做,因为在我的语言中,<a native C file handle>不是合法类型的,因此不能写入ValueArray。
我能想到的一个解决办法是:
// Use vm_instantiate_class to create an instance, and do actual initialization
// ad-hoc in the outside code.
ValueArray args = make_value_array(); // Empty args
ObjectInstance* instance = vm_instantiate_class(file_class, args);
ObjectInstanceFile* file_object = (ObjectInstanceFile*) instance;
file_object->handle = <a native C file handle>; // Initialize the field这种方法是可行的,但它并不优雅。
我希望能够在包装本机资源的类上调用vm_instantiate_class (在语言级别上没有等效资源),并且使它能够工作,而不必在调用之后对实例进行进一步修补。
这种事情是如何在语言实现中普遍实现的呢?具体来说,我想了解CPython实现,但否则任何语言实现示例都可以。
发布于 2020-04-17 21:41:44
我不确定CPython,但是Lua解释器(和亲属)所做的解决方案是有一个与void * +方法相对应的对象类型(Lua调用这个用户数据)。Lua没有构造函数,所以它们所需要的只是一个有效返回包装器的make_userdata(void *, methods)函数。通过在args到vm_instantiate_class中传递这样的用户数据(它甚至不需要支持方法,它实际上只是指针的包装器),这可以很容易地适应您的情况。
另一方面,您有构造函数(@init)。为什么不在构造函数中打开句柄呢?然后,它可以设置自己的方式,它想要。所有东西都被很好地封装,而且,作为一个额外的好处,对象实际上是可以从用户代码中实例化的,这在当前系统中似乎不是这样的。
发布于 2020-04-18 06:53:56
在滑翔和GObject的源代码中寻找灵感(来自GTK)。阅读有关ObjVLisp模型的论文。
在RefPerSys中,我们面临着同样的问题,这也与垃圾收集垃圾收集密切相关。
假设您的目标平台具有相同大小的所有指针(指向数据的指针和指向函数的指针),具有单一的平面虚拟地址空间和冯·诺依曼 (而不是哈佛大学)体系结构。在2020年,大多数操作系统和处理器(但不是全部)都是如此: Linux/x86-64、Windows/x86-64、Android/ARM等等.
然后,您可以将任何指针(不安全地)从void*或向转换。因此,您的file_object->handle将是(例如)一个适当地从/到一个void*转换的FILE*。你会得到有标记的工会。
https://softwareengineering.stackexchange.com/questions/408676
复制相似问题