I am trying to apply a prototype to a copy of a subroutine, without modifying the existing subroutine. I.e. this is not ok:
use Scalar::Util 'set_prototype';
sub foo {};
*bar = \&foo;
set_prototype(\&bar, '$'); # also modifes "foo"
What I want to achive can be done with a goto &sub
:
sub foo {};
sub bar($) {
goto &foo;
}
However, this is introduces unnecessary overhead which I am not keen on. Therefore my question: Is there a way to make a (shallow) copy of a subroutine (CV), so that setting the prototype of the copy does not affect the original? I.e. something like
use Scalar::Util 'set_prototype';
sub foo {};
*bar = magical_cv_copy(\&foo);
set_prototype(\&bar, '$'); # does not modify "foo"
I looked at Sub:Clone
, but it appears to be out of date and won't install on my system without forcing it. I would prefer not having to write XS code for this.
use strict;
use warnings;
use Test::More tests => 7;
use Scalar::Util qw/refaddr set_prototype/;
sub foo {
my ($x) = @_;
return 40 + $x;
}
*bar = then_a_miracle_occurs(\&foo);
ok not(defined prototype \&foo), 'foo has no prototype';
ok not(defined prototype \&bar), 'bar has no prototype';
isnt refaddr(\&foo), refaddr(\&bar), 'foo and bar are distinct';
set_prototype \&bar, '$';
ok not(defined prototype \&foo), 'foo still has no prototype';
is prototype(\&bar), '$', 'bar has the correct prototype';
is foo(2), 42, 'foo has correct behavior';
is bar(2), 42, 'bar has correct behavior';
sub then_a_miracle_occurs {
my ($cv) = @_;
# what goes here?
# return sub { goto &$cv }
}
My X-Problem is that a 3rd-party module defines some function foo
without prototypes. Judicious use of prototypes can make this function more elegant to use, so I want to create a copy of that sub, except that it does have a prototype. I cannot make any assumptions about the foo
function – it may be also be an XS subroutine.
I cannot directly set the prototype of foo
, because I do not wish to interfere with other modules that rely on the original behavior of foo
.
So we arrive at my Y-Problem: how to copy a subroutine.
The miracle function is probably the internal cv_clone
.
You mentioned Sub::Clone
, and it seems to do what you want. It comes with a pure-Perl implementation based on the goto
trick you described, and an XS implementation that calls cv_clone
.
I can't find another module that wraps this internal function. If you have trouble installing the module, I'd suggest you open a RT ticket. There's one older but unresolved ticket already, so you might have to nudge one of the maintainers.
Ideally, this functionality would be part of a module like Sub::Util
. We already have Scalar::Util
, List::Util
, Hash::Util
, but nothing for subroutines.
a 3rd-party module defines some function foo without prototypes. Judicious use of prototypes can make this function more elegant to use, so I want to create copy of that sub, except that it does have a prototype.
All you need is a thin wrapper:
sub foo(&@) { &Real::foo }
or
sub foo(&@) { goto &Real::foo }
The difference is that the latter hides the call to your foo
, which makes a difference if Real::foo checks its caller (e.g. builds a stack trace on error).
If your idea of optimization is getting rid of a sub call, you're doing it wrong.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With