方法修饰符和glob分配

时间:2015-09-30 16:23:37

标签: perl moose

有时,Moose的方法修饰符不能很好地与其他试图创建的符号表条目一起使用,这些包试图以自己的方式做类似Moose的事情。

我正在使用一些遵循此模式的旧代码:

package MethodCreator;

sub make_some_method {
    my $caller = caller();
    *{$caller . '::generated_method'} = sub { print 'I am a generated method' }
}

1;

MethodCreator包的目的是向多个使用者包添加一些标准定义,并通过直接全局赋值实现这一点。问题是,这些创建的方法不适用于Moose的方法修饰符:

package Consumer;

use Moose;
use MethodCreator;

MethodCreator::make_some_method();

# The following line causes compilation to fail                                                                                                                                                              
# before generated_method => sub { print 'About to call a generated method: ' };                                                                                                                             

generated_method();

1;

如注释所示,尝试在其中一个动态添加的子例程上使用方法修饰符会导致编译时错误(“generated_method不在继承层次结构中”)。

更改或替换MethodCreator是不切实际的(尽管可能是“正确的解决方案”)。所以问题是:如何更改包装消费者以使'之前'修饰符与这些子例程很好地配合,即如果直接在消费者中定义'generated_method',你的行为就像你期望的那样?

3 个答案:

答案 0 :(得分:1)

您可以使用Class::Method::Modifiers来执行此操作,而不是使用内置的Moose修改器。但是,请确保您不从中导入任何内容,否则您将收到重新定义的警告。而是从Class :: Method :: Modifiers包中显式调用float textFieldHeight = 42; float spacing = 8; float width = 300; for(int k = 0; k < [array count]; k++) { UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(x, i*(textFieldHeight + spacing), width, textFieldHeight)]; // ... other things you'd like to do with the textField [textFieldArray addObject:textField]; }

before

输出:

package MethodCreator;
use strict;
use warnings;
no strict 'refs';

sub make_some_method {
    my $caller = caller();
    *{$caller . '::generated_method'} = sub { print 'I am a generated method' }
}

package Consumer;

use Moose;
use Class::Method::Modifiers ();

MethodCreator::make_some_method();

# This one works
Class::Method::Modifiers::before( generated_method => sub { print 'About to call a generated method: ' });

generated_method();

1;

现在为什么这样做?

The C::M::M docs这样说:

  

请注意,这些修饰符的语法和语义直接从Moose借用(但实现方式不是)。

简化后,这将使用自己的sub覆盖包中的sub,后者执行你的东西并随后调用原始的sub。

另一方面,在Moose中,它们是在Class :: MOP :: Method :: Wrapped中实现的,它们使用MOP来处理所有花哨的继承内容以及多个修饰符。但是因为你没有那些带有'手动生成'子程序/方法的那些,它们将无法工作。

答案 1 :(得分:1)

编译子程序包与子程序相关联。

$ perl -e'
   package Abc;
   use Devel::Peek;
   Dump(sub { });
'
SV = IV(0xf54988) at 0xf54998
  REFCNT = 1
  FLAGS = (TEMP,ROK)
  RV = 0xf54a88
  SV = PVCV(0xf70aa8) at 0xf54a88
    REFCNT = 2
    FLAGS = (PADMY,ANON,WEAKOUTSIDE,CVGV_RC)
    COMP_STASH = 0xf72818       "Abc"
    START = 0xf7a5d0 ===> 0
    ROOT = 0xf7a6e0
    GVGV::GV = 0xf80b68 "Abc" :: "__ANON__"       <-----
    FILE = "-e"
    DEPTH = 0
    FLAGS = 0x490
    OUTSIDE_SEQ = 94
    PADLIST = 0xf72830
    PADNAME = 0xf72848(0xf7ab90) PAD = 0xf72998(0xfacb90)
    OUTSIDE = 0xf54bd8 (MAIN)

显然,穆斯的before会对此进行检查。您无法更改此内容,因此如果您想继续使用Moose的make_some_method,则必须按如下所示更改before

sub _install_method {
    my ($pkg_name, $sub_name, $sub) = @_;
    eval("
       package $pkg_name;
       sub $sub_name { &\$sub }
       return 1;
    ")
       or die($@);
}

sub make_some_method {
   _install_method(
      scalar(caller()),
      generated_method => sub {
         print 'I am a generated method';
      },
   );
}

答案 2 :(得分:0)

你能否认为$caller是Moose类?[1]如果是这样,你可以用Moose方式创建方法,

$caller->meta->add_method(generated_method => sub {
    print "I am a generated method\n";
});

如果执行代码的代码可以依赖于Moose,但调用代码可能是也可能不是Moose类,你可以说

Class::MOP::Class->initialize($caller)->add_method(generated_method => sub {
    print "I am a generated method\n";
});

,这适用于任何类型的Perl类。

[1]或Moo课程。但不是Moose或Moo类继承自非Moose非Moo类!