通过预定义的哈希向Class :: XSAccessor添加访问器?

时间:2014-07-15 03:23:05

标签: perl hash accessor

考虑以下示例:

use 5.010;
use Data::Dumper;

{
  package MyTestPck;
  use Data::Dumper;
  use Tie::IxHash;
  sub ordered_hash { # http://stackoverflow.com/a/3001400/277826
    tie my %hash => 'Tie::IxHash';
    %hash = @_;
    \%hash
  }
  my $fields = ordered_hash(
          (map { $_ => $_ } (qw(
          varA
          varB
          varC
          )))
    );
  print Dumper($fields);
  use Class::XSAccessor
      accessors => {
        %{$fields}
      };
  sub new {
    my $class = shift;
    my @set_arr = @_; # the rest
    my $ic = 0; my $self = {};
    bless $self, $class;
    for my $k (keys %{$fields}) {
      my $fld = $fields->{$k};
      my $val = $set_arr[$ic];
      print("k $k fld $fld ic/val $ic $val\n");
      $self->$fld($val);
      $ic++;
    }
    return $self;
  }
  1;
}

my $pcktestobj = MyTestPck->new((10, 20, 30));
print Dumper($pcktestobj);

如果我按原样运行;代码失败了:

$ perl /tmp/test.pl
$VAR1 = {
          'varA' => 'varA',
          'varB' => 'varB',
          'varC' => 'varC'
        };
k varA fld varA ic/val 0 10
Can't locate object method "varA" via package "MyTestPck" at /tmp/test.pl line 34.

但是,如果用以下代码替换代码的中间部分:

...
  use Class::XSAccessor
      accessors => ordered_hash(
          (map { $_ => $_ } (qw(
          varA
          varB
          varC
          )))
    );
...

...然后输出符合预期:

$ perl /tmp/test.pl
$VAR1 = {
          'varA' => 'varA',
          'varB' => 'varB',
          'varC' => 'varC'
        };
k varA fld varA ic/val 0 10
k varB fld varB ic/val 1 20
k varC fld varC ic/val 2 30
$VAR1 = bless( {
                 'varC' => 30,
                 'varB' => 20,
                 'varA' => 10
               }, 'MyTestPck' );

所以,显然,我不能通过预定义的哈希设置访问器,如use Class::XSAccessor accessors => { %{$fields} }; - 我显然必须指定内联的访问器哈希。

但为什么会这样呢? Class::XSAccessor docs中是否有任何内容可以提醒我这种行为?有没有办法可以使用预定义字段(我在第一个示例中的意图)使用Class::XSAccessor

1 个答案:

答案 0 :(得分:1)

use语句在编译时执行,但您的哈希不会在运行时之前填充。看看这个例子:

my $x = {};        # this statement is executed **SECOND**
use strict;        # this statement is executed **FIRST**

因此,您无法在上面$x语句中使用use变量。 <{1}}尚未定义。

幸运的是,$x语句可以轻松分解为use后跟require。每个都发生在运行时。所以尝试替换它:

import

有了这个:

use Class::XSAccessor
   accessors => {
      %{$fields}
   };

另一种方法是强制require Class::XSAccessor; Class::XSAccessor->import( accessors => $fields ); 在编译时填充。这应该有效,但不太漂亮:

$fields