如何获得被调用别名子程序的名称?

时间:2018-07-12 16:14:43

标签: perl

如何得知使用哪个别名来调用别名子例程? caller给出了原始的sub名称,但我想查看呼叫时使用的名称。

示例:

use 5.010;
sub x_y_z {
  return ( caller(0) )[3];
}

*foo_bar_baz = \&x_y_z;

say x_y_z();        # x_y_z
say foo_bar_baz();  # x_y_z, but need foo_bar_baz

编辑以解决XY问题

我添加了另一个示例来展示我的更深层次的意图。我想创建调度表来路由一些任务:

my $dispatch = {
  x => {
    y => {
      z => sub {
          &x_y_z;
      },
    }
  },
  a => {
    b => {
      c => {
        d => sub {
          &a_b_c_d;
        },
      }
    }
  }
}

sub foo {
  my @arg = ( split '_', ( split( '::', ( caller(0) )[3] ) )[1] );  
  return @arg;
}

*x_y_z = \&foo;
*a_b_c_d = \&foo;

您可能会想到,这棵树可能长得很大。现在,调度树中的许多叶子需要基本相同的子对象,它们的命名方式不同(它们的命名方式不同),我希望只包含一个子对象,并为特定任务添加别名。

2 个答案:

答案 0 :(得分:4)

不能。 foo_bar_baz是别名。 caller报告所声明的子例程的名称,而不是调用该子例程的名称。请注意,并非所有子例程都具有名称,并且并非所有调用都按名称。 (匿名子仅作为CODE引用存在;它们在符号表中没有条目。任何命名或未命名的子都可以通过引用来调用。)

也就是说,这里不需要别名。您真正想要的是子程序应该在其上运行的数据库,表等的额外参数。惯用的方法是包装通用子并通过包装传递该信息:

my %dispatch = (
    a => { b => { c => sub { foo('a', 'b', 'c', @_) } } },
    x => { y => { z => sub { foo('x', 'y', 'z', @_) } } },
);

$dispatch{a}{b}{c}->('foo');
$dispatch{x}{y}{z}->('bar');

sub foo {
    my $db     = shift;
    my $table  = shift;
    my $task   = shift;
    my @params = @_;
    say "$db $table $task: @params";
}

答案 1 :(得分:4)

您试图做的事在Perl的数据模型中根本不可能实现。别名只是别名,不是具有自己标识的对象。

请注意,可以复制子例程并为其重新命名,例如:

use Sub::Name;

*x_y_z = subname x_y_z => \&foo;

但是您将必须手动执行此操作。

除了堆栈跟踪以外,不要依赖任何子名称。试图在这些名称之上构建任何逻辑都可能会导致难以调试的混乱,而不是优雅的软件。

最好将路由名作为显式参数传递给处理程序函数,并创建一个辅助函数以对必要的管道进行抽象。例如:

my %routes;

sub route {
  my ($name, $handler) = @_;
  $routes{$name} = sub { $handler->($name => @_) };
  return;
}

sub common_handler { ... }

route a_b_c => \&common_handler;
route x_y_z => \&common_handler;
route foo_bar => sub {
  my ($route) = @_;
  say "Custom handler invoked for route $route";
};

$routes{$name}->(@args);

如果绝对必要,您当然可以实现这样的route函数,以便它将处理程序作为命名子例程安装。但是到那时,您正在构建某种类似于Moo(se)的框架,而不是普通的Perl模块。