我可以覆盖用C编写的Ruby方法吗?

时间:2011-08-21 13:51:34

标签: c ruby override

是否有可能使用Ruby代码覆盖使用C语言编写的Ruby本身的方法,例如rb_error_frozen

背景:我想知道在修改冻结对象时是否可以让Ruby仅记录警告,而不是引发异常。这样,我可以记录各种状态修改,而不是在第一次修改时停止。

我主要考虑使用YARV来做这件事,但如果这样做更容易,我可以使用另一个实现。

是的,这是一个崭新的项目!不要在生产环境中尝试这个!

2 个答案:

答案 0 :(得分:5)

我只能说MRI / YARV,但我会试一试。如果已将C函数显式定义为Ruby对象上的方法,则只能覆盖Ruby中源自C的函数。例如,Kernel#extend在C中明确定义为

rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1);

因为C函数rb_obj_extend已经与Ruby世界中的方法Kernel#extend“链接”(在引号中,因为我用比喻的方式来说,我不是指C链接)理论上,如果覆盖rb_obj_extend,您可以覆盖Kernel#extend的行为。

我会说,鉴于以下两个条件,您可以声称您实际上“覆盖”了rb_ * C函数:

  • rb_ * C函数已与某些Ruby对象“链接”,因此我们在Ruby世界中有一个句柄,可以作为我们可以覆盖的钩子
  • 给定的rb_ *方法仅用于这一点,正是为了这个目的,它没有在其他地方重复使用

现在,如果你看rb_error_frozen,它就不会满足这两个条件。它是C实现中的一个帮助器,这意味着它是从几个地方调用的。并且它没有与任何Ruby对象明确地“链接”,因此你没有可以覆盖它的钩子。

但是,并非所有人都失去了。你不能直接覆盖rb_error_frozen,但你仍然可以尝试覆盖rb_error_frozen冒泡到“Ruby表面”的所有Ruby方法。我的意思是你可以检查C源代码中使用rb_error_frozen的所有位置,并从这些位置尝试找到可以触发这些代码的每个Ruby方法。如果这是一个封闭的集合,您可以简单地覆盖所有这些方法,以“事实上覆盖”rb_error_frozen的行为。

然而,这只是一个拼凑的解决方案。如果有人决定再写一个C扩展名,他们再次直接拨打rb_error_frozen,那么你所有的努力都会丢失。

长话短说:如果已经明确定义为Ruby对象的某些方法的实现,例如,只能覆盖C函数。如在

rb_define_method(rb_cString, "gsub", rb_str_gsub, -1);

您可以假设它只会用于此目的。但即便如此,你也不是100%安全,有人仍然可以决定在C代码的其他部分重用该功能。


编辑:你说过你只想在修改冻结对象时警告Ruby而不是加注。我刚刚查看了源代码,看看是否可以覆盖调用rb_error_frozen的所有位置。问题是rb_check_frozen - 它被称为修改对象的任何地方(因为它应该是),并再次调用rb_error_frozen。这种机制深深植根于C内部,并没有在任何地方的Ruby表面上发布,所以没有办法覆盖“提升行为”或至少没有任何不需要大量工作的方法。如果你想一分钟实际上是一件好事。如果可以简单地覆盖行为,那么实际上这可能被视为Ruby实现中的安全漏洞。冻结一个物体应该保证它无论如何都保持不可修改。

答案 1 :(得分:3)

是的,当然你可以覆盖用C(或Java或C#或C ++或Objective-C或ECMAScript或其他)实现的Ruby方法。

由于Ruby是一种面向对象的语言,因此您可以在Ruby中执行的事件是覆盖或创建 new 方法,因为在两个最广泛使用的Ruby实现(MRI和YARV)所有方法都在C中实现,而在Ruby中实现 none ,你根本就无法实现如果您无法覆盖用C语言编写的方法,那么任何事情

然而,存在一个大问题:rb_error_frozen不是Ruby方法。这是一个C函数。