关于Rational :: compatible和Complex ::兼容的内容是什么?

时间:2016-02-20 20:52:44

标签: ruby

任何人都可以解释中提到的(子)类的存在 主题?

_

Rational::constants false
# [:compatible]

Rational::compatible
# NoMethodError: undefined method 'compatible' for Rational:Class

Rational::const_get 'compatible'
# NameError: wrong constant name compatible

感谢。

1 个答案:

答案 0 :(得分:4)

我相信这个类的存在是为了Marshal向后兼容旧版本的Ruby。换句话说,它使得在一个版本的Ruby中Marshal.load可以加载在旧版本的Ruby中编组的复杂和Rational对象。

您可以在第二行(line 2,446 in rational.c)看到Rational :: compatible类的定义位置:

rb_define_private_method(rb_cRational, "marshal_dump", nurat_marshal_dump, 0);
compat = rb_define_class_under(rb_cRational, "compatible", rb_cObject);
rb_define_private_method(compat, "marshal_load", nurat_marshal_load, 1);
rb_marshal_define_compat(rb_cRational, compat, nurat_dumper, nurat_loader);

我的C很糟糕,但对我而言,这四行看起来分别如下:

  1. Rational#marshal_dump私有方法定义为指向C函数nurat_marshal_load的指针。
  2. 定义compatible类(Rational)下的rb_cRational类。我认为第三个参数rb_cObject是超类。
  3. Rational#marshal_load定义为C函数nurat_marshal_dump
  4. 这是一个有趣的问题:它使用四个参数调用rb_marshal_define_compat:Rational类(rb_cRational);刚刚定义的Rational :: compatible类(compat);以及nurat_dumpernurat_loader
  5. 从同一文件中的line 1,629开始,您可以看到nurat_dumpernurat_loader(以及nurat_marshal_dumpnurat_marshal_load)的定义位置。使用git blame的魔力,我们可以确切地看到nurat_dumpernurat_loader的定义时间:2012年7月25日在Subversion中提交#36538。这是提交消息:

      

    complex.c,rational.c:兼容的编组加载程序

         
        
    • complex.crational.c:与1.8兼容的兼容编组加载程序。 [ruby-core:45775] [Bug#6625]
    •   

    现在我们到了某个地方! Bug #6625就是这个:

      

    在复杂和理性

    上破坏了元帅的兼容性      

    由1.8或更早版本转储的Complex和Rational无法由trunk加载。

    它继续描述一个测试用例,并包含上面提交中看到的补丁。

    那么,也许你想知道的是rb_marshal_define_compat?你可以看到它defined in marshal.c。就像我说的那样,我的C很糟糕,但我认为它的作用是把它的参数 - newclass(即Rational),oldclass(Rational :: compatible),dumper和{{ 1}} - 进入marshal_compat_t struct并将其插入loader。稍后,当您尝试compat_allocator_tblMarshal.dump对象时,它将在该表中查看是否存在兼容性类,并在必要时使用其“dumper”或“loader”。 p>

    至于为什么Rational :: compatible没有以大写字母命名并且无法通过常规方式访问,我只能猜测这是设计使得它只能由Ruby在内部访问。

    但是,您可以使用ObjectSpace获取对该类的引用,但您无法使用它:

    Marshal.load