2012-01-02 29 views
7

最近我一直在练习一些Ruby元编程,并且想知道assigning anonymous classes to constants匿名类被分配给常量时是否存在钩子?

在Ruby中,可以按照以下步骤创建一个匿名类:

anonymous_class = Class.new # => #<Class:0x007f9c5afb21d0> 

这个类的新实例可以被创建:现在

an_instance = anonymous_class.new # => #<#<Class:0x007f9c5afb21d0>:0x007f9c5afb0330> 

,当匿名类被分配给一个常量,类现在有一个合适的名字:

Foo = anonymous_class # => Foo 

,先前创建的实例现在也是类的一个实例:

an_instance # => #<Foo:0x007f9c5afb0330> 

我的问题:是否有当一个匿名类被分配到一个恒定的时刻挂钩的方法?

Ruby中有很多hooks methods,但是我找不到这个。

+0

非常有趣的问题。 – 2012-01-02 20:36:29

+1

AFAIK仍然没有变量赋值,常量或其他方法([很长一段时间在这里看到相同的问题](http://www.ruby-forum.com/topic/65720))。全局,是的。 – 2012-01-02 21:14:56

回答

6

让我们来看看常量赋值如何在内部工作。下面的代码是从ruby-1.9.3-p0的源代码tarball中提取的。首先我们来看看虚拟机指令setconstant(这是用来分配常数)的定义:

# /insns.def, line 239 
DEFINE_INSN 
setconstant 
(ID id) 
(VALUE val, VALUE cbase) 
() 
{ 
    vm_check_if_namespace(cbase); 
    rb_const_set(cbase, id, val); 
    INC_VM_STATE_VERSION(); 
} 

没有机会把钩在vm_check_if_namespaceINC_VM_STATE_VERSION这里。所以,我们看rb_const_setvariable.c:1886年),被称为每次恒定分配的功能:

# /variable.c, line 1886 
void 
rb_const_set(VALUE klass, ID id, VALUE val) 
{ 
    rb_const_entry_t *ce; 
    VALUE visibility = CONST_PUBLIC; 

    # ... 

    check_before_mod_set(klass, id, val, "constant"); 
    if (!RCLASS_CONST_TBL(klass)) { 
     RCLASS_CONST_TBL(klass) = st_init_numtable(); 
    } 
    else { 
     # [snip], won't be called on first assignment 
    } 

    rb_vm_change_state(); 

    ce = ALLOC(rb_const_entry_t); 
    ce->flag = (rb_const_flag_t)visibility; 
    ce->value = val; 

    st_insert(RCLASS_CONST_TBL(klass), (st_data_t)id, (st_data_t)ce); 
} 

我删除了所有甚至没有所谓的第一次恒定分配的代码在模块内部。然后,我查看了这个函数调用的所有函数,但没有找到可以从Ruby代码放置钩子的单个点。这意味着难以理解的是,除非我遗漏了某些东西,否则将会有一种挂钩常量分配的方式(至少在MRI中)。

更新

澄清:匿名类不神奇尽快得到一个新的名字,因为它被分配(如安德鲁的回答正确地指出)。相反,常量名和类的对象ID存储在Ruby的内部常量查找表中。如果在此之后请求了课程名称,现在可以解析为专有名称(而不仅仅是Class:0xXXXXXXXX...)。

因此,对此作业做出最好的反应是在背景工作线程的循环中检查类的name,直到它不是nil(这是一个巨大的资源浪费,恕我直言)。

+0

感谢您提供非常广泛的答案。为了完成它,你是如何找到对'setconstant'的引用的?源文件('insns.def')对我来说不是很清楚:“设置常量变量id。如果klass是Qfalse,常量可以在此范围内访问。如果klass是Qnil,则设置顶级常量。否则,在klass类或模块下设置常量。“这段代码是否确实用于将常量分配给(匿名)类实例? – rdvdijk 2012-01-02 22:22:36

+0

@rdvdijk:我猜测并用'printf'对其进行了验证。 – 2012-01-02 22:24:14

+0

我已经查看了您在此处引用的源代码,并了解了您粘贴的代码的含义。但是,它并没有完全回答我的问题。此代码显示常量赋值的处理,并且此代码没有可用的钩子。但是,这并不能解释匿名类何时何地发现它的新名称。根据Andrew Grimm的回答,似乎'classname'和相关代码在'RCLASS_CONST_TBL'或'rb_class_tbl'中查找常量,对吧? – rdvdijk 2012-01-03 11:53:40

0

当匿名类被分配给一个常量时,实际上并不会获得它们的名字。当他们接下来问他们的名字是什么时,他们实际上得到了它。

我会尽力为此找到一个参考。 编辑:找不到一个,对不起。

+0

关键是在'variable.c:305'(ruby-1.9.3-p0)中定义的函数'rb_class_name'和它所调用的函数(特别是'find_class_path',它使用'RCLASS_CONST_TBL'映射来检查类是绑定到一个名称,至少如果我的解释是正确的)。 – 2012-01-02 21:09:13

相关问题