Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you take a reference of a builtin function in Perl?

Tags:

syntax

perl

What syntax, if any, is able to take a reference of a builtin like shift?

$shift_ref = $your_magic_syntax_here;

The same way you could to a user defined sub:

sub test { ... }

$test_ref = \&test;

I've tried the following, which all don't work:

\&shift
\&CORE::shift
\&{'shift'}
\&{'CORE::shift'}

Your answer can include XS if needed, but I'd prefer not.

Clarification: I am looking for a general purpose solution that can obtain a fully functional code reference from any builtin. This coderef could then be passed to any higher order function, just like a reference to a user defined sub. It seems to be the consensus so far that this is not possible, anyone care to disagree?

like image 682
Eric Strom Avatar asked Oct 18 '09 17:10

Eric Strom


3 Answers

No, you can't. What is the underlying problem you are trying to solve? There may be some way to do whatever that is.

Re the added part of the question "Your answer can include XS if needed, but I'd prefer not.", calling builtins from XS is really hard, since the builtins are set up to assume they are running as part of a compiled optree and have some global variables set. Usually it's much easier to call some underlying function that the builtin itself uses, though there isn't always such a function, so you see things like:

buffer = sv_2mortal(newSVpvf("(caller(%d))[3]", (int) frame));
caller = eval_pv(SvPV_nolen(buffer), 1);

(doing a string eval from XS rather than go through the hoops required to directly call pp_caller).

like image 102
ysth Avatar answered Oct 22 '22 23:10

ysth


I was playing around with general purpose solutions to this one, and came up with the following dirty hack using eval. It basically uses the prototype to pull apart @_ and then call the builtin. This has only been lightly tested, and uses the string form of eval, so some may say its already broken :-)

use 5.10.0;
use strict;
use warnings;

sub builtin {
    my ($sub, $my, $id) = ($_[0], '');
    my $proto = prototype $sub         //
                prototype "CORE::$sub" //
                $_[1]                  //
                ($sub =~ /map|grep/ ? '&@' : '@;_');
    for ($proto =~ /(\\?.)/g) { $id++;
        if (/(?|(\$|&)|.(.))/) {
            $my  .= "my \$_$id = shift;";
            $sub .= " $1\$_$id,";
        } elsif (/([@%])/) {
            $my  .= "my $1_$id = splice \@_, 0, \@_;";
            $sub .= " $1_$id,";
        } elsif (/_/) {
            $my  .= "my \$_$id = \@_ ? shift : \$_;";
            $sub .= " \$_$id,"
        }
    }
    eval "sub ($proto) {$my $sub}"
        or die "prototype ($proto) failed for '$_[0]', ".
               "try passing a prototype string as \$_[1]"
}

my $shift = builtin 'shift';
my @a = 1..10;
say $shift->(\@a);
say "@a";

my $uc = builtin 'uc';
local $_ = 'goodbye';
say $uc->('hello '), &$uc;

my $time = builtin 'time';
say &$time;

my $map = builtin 'map';
my $reverse = builtin 'reverse';
say $map->(sub{"$_, "}, $reverse->(@a));

my %h = (a=>1, b=>2);
my $keys = builtin 'keys';
say $keys->(\%h);

# which prints
# 1
# 2 3 4 5 6 7 8 9 10
# HELLO GOODBYE
# 1256088298
# 10, 9, 8, 7, 6, 5, 4, 3, 2, 
# ab

Revised with below and refactored.

like image 32
Eric Strom Avatar answered Oct 23 '22 00:10

Eric Strom


You could do this if you patched the internal method first (which would give you the coderef of your patch):

use strict;
use warnings;

BEGIN {
    *CORE::GLOBAL::die = sub { warn "patched die: '$_[0]'"; exit 3 };
}

print "ref to patched die: " . \&CORE::GLOBAL::die . "\n";
die "ack, I am slain";

gives the output:

ref to patched die: CODE(0x1801060)
patched die: 'ack, I am slain' at patch.pl line 5.

BTW: I would appreciate if anyone can explain why the override needs to be done as *CORE::GLOBAL::die rather than *CORE::die. I can't find any references for this. Additionally, why must the override be done in a BEGIN block? The die() call is done at runtime, so why can't the override be done at runtime just prior?

like image 1
Ether Avatar answered Oct 22 '22 22:10

Ether