simbabque
simbabque

Reputation: 54373

How can I prevent a sub being overwritten in Perl?

I'm working with legacy code and have to require a .pl file that defines a sub foo. My problem is that in my main:: namespace there already is another sub foo, which is called later in a part of the program I'm currently not dealing with.

The file I require defines sub foo {} because obviously it does not want the foo things to happen where it is usually called. In my case, that is bad.

I've tried playing around with the *foo glob:

*old_foo = *foo;
require 'foo_killer.pl';
*foo = *old_foo;

Of course, that doesn't work since I've only created an alias (as brian d foy points out on page 133 of Mastering Perl) and thus *old_foo will point to the now 'empty' subroutine.

Is there a way to somehow copy what's in *foo{CODE} to somewhere else instead of aliasing it? Or is there maybe another approach to solve this?

Upvotes: 5

Views: 142

Answers (3)

Galimov Albert
Galimov Albert

Reputation: 7357

Try like this

{
    local *foo;
    require 'foo_killer.pl';
}

Upvotes: 3

Dallaylaen
Dallaylaen

Reputation: 5318

I would suggest wrapping the evil legacy code into a package once and for all.

package Foo;
use strict; 
use warnings;

use Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(foo bar $evil $global $variables);

do "foo_killer.pl"
     or die "Failed to load foo_killer.pl: ".($@ || $!);
1;

Here I use do, because require does nothing if the code is required elsewhere. (We had ugly require "//path/to/code.pl" because of this!)

This way you can decide whether to load foo via use Foo qw(foo); or use Foo qw(bar);.

UPDATE: Oh, and you'd better calculate path to foo_killer.pl relatively to __FILE__ and not load it by absolute path:

my $foo_killer = __FILE__; # $LIB/Foo.pm
$foo_killer =~ s,(/+[^/]+),legacy,; # $LIB/legacy
$foo_killer .= "foo_killer.pl"; # $LIB/legacy/foo_killer.pl
# now do $foo_killer;

It's up to you (and your team) though.

Upvotes: 2

simbabque
simbabque

Reputation: 54373

Figured it out myself. I have to use the CODE portion of the typeglob instead of assigning the whole typeglob to another typeglob. That way it seems to make a copy.

*old_foo = *foo{CODE};
require 'foo_killer.pl';
*foo = *old_foo{CODE};

brian d foy also talks about this in Mastering Perl (on page 131f), but doesn't mention the copying part.

Upvotes: 4

Related Questions