• 2007-01-15

    管窥Ruby——包含模块

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://dreamhead.blogbus.com/logs/4302773.html

    在《管窥Ruby——类层次结构》中,我们已经见识过Ruby是如何实现包含一个模块的,不过,那里的侧重点不同,我们忽略了很多东西。

    让我们回顾一下那段代码:
    void
    rb_include_module(klass, module)
        VALUE klass, module;
    {
        VALUE p, c;
        int changed = 0;

        rb_frozen_class_p(klass);
        if (!OBJ_TAINTED(klass)) {
            rb_secure(4);
        }
      
        if (NIL_P(module)) return;
        if (klass == module) return;

        if (TYPE(module) != T_MODULE) {
            Check_Type(module, T_MODULE);
        }

        OBJ_INFECT(klass, module);
        c = klass;
        while (module) {
            int superclass_seen = Qfalse;

            if (RCLASS(klass)->m_tbl == RCLASS(module)->m_tbl)
                rb_raise(rb_eArgError, "cyclic include detected");
            /* ignore if the module included already in superclasses */
            for (p = RCLASS(klass)->super; p; p = RCLASS(p)->super) {
                switch (BUILTIN_TYPE(p)) {
                    case T_ICLASS:
                        if (RCLASS(p)->m_tbl == RCLASS(module)->m_tbl) {
                            if (!superclass_seen) {
                                c = p; /* move insertion point */
                            }
                           goto skip;
                        }
                        break;
                    case T_CLASS:
                        superclass_seen = Qtrue;
                        break;
                }
            }
            c = RCLASS(c)->super = include_class_new(module, RCLASS(c)->super);
            changed = 1;
        skip:
            module = RCLASS(module)->super;
        }
        if (changed) rb_clear_cache();
    }
    (class.c)

    经过这段代码的处理,会生成一个包含类(include class),它会加到原来的体系中,就像下面这个样子:
        super class
               |
        include class
               |
            klass


    包含类的iv_table和m_table就指向了模块的iv_table和m_table,通过这种方法,模块内的内容就可以在类中访问到了。

    我们注意到,这段代码并不像这里说得那么简单,至少还有两个循环在那里。先来看看外面的while循环,简化之:
    while (module) {
        ...
        module = RCLASS(module)->super;
    }

    这段代码告诉我们,我们是沿着模块的继承体系一路向上的……

    慢着,模块的继承体系?模块也有继承吗?至少在Ruby的层次上不记得有这种说法。事实上,在Ruby的层次上确实没有,你不会去让一个模块继承另一个模块。那么模块的继承体系从何谈起呢?

    我们知道,不仅是类可以包含模块,模块也可以包含模块。调用的当然也是这里的代码,由此可见,这个函数的第一个参数(klass)命名并不像看上那么完美。如果模块包含类模块,走这段代码,其结果就是这个样子:
         include module
                 |
            module

    所以,虽然在Ruby的层次上模块没有继承,但是在C的层次上,它也是可以有超类的。回到原来的路上,沿着模块的继承体系一路向上,就可以把一个模块所包含的所有模块都加入这个类的继承体系之中。

    再来看看内部的for循环,简化之:
    for (p = RCLASS(klass)->super; p; p = RCLASS(p)->super) {
        if (p已经包含过) {
            略过
        }
    }

    正如前面的注释所写,这段代码所要做的就是忽略已经包含的模块,这是一个经济实惠的选择。此外,这里还有一段代码:
    if (RCLASS(p)->m_tbl == RCLASS(module)->m_tbl) {
        if (!superclass_seen) {
            c = p;    /* move insertion point */
        }
        ...
    }

    注释告诉我们,这里移动了插入点,这段代码保证了插入包含类之后的结构依然能够保证各包含类之间顺序的一致性。

    谈及include_class_new时,我们曾经忽略了一段代码:
    if (TYPE(module) == T_ICLASS) {
        RBASIC(klass)->klass = RBASIC(module)->klass;
    }
    else {
        RBASIC(klass)->klass = module;
    }
    (class.c)

    这里,我们将新生成的包含类的klass字段设置为对应module,也就是建立起包含类与对应模块之间的联系。其实,这里设置klass多少有些偏离这个字段本身的含义,但对于包含类来说,把设置这个字段也算是“废物利用”,节省了一些空间。Module#ancestors方法(实现参见class.c的rb_mod_ancestors),正是利用了这个特点,去寻找一个模块的“祖先”。





    引用地址:

发表评论

您将收到博主的回复邮件
记住我