views:

187

answers:

3

I have some Perl modules which exports various functions. (We haven't used @EXPORT in new modules for some years, but have retained it for compatibility with old scripts.)

I have renamed a number of functions and methods to change to a consistent naming policy, and thought that then adding a list of lines like

*directory_error      = *directoryError;

at the end of the module would just alias the old name to the new.

This works, except when the old name is exported, and a calling script calls the function with an unqualified name: in this case, it reports that the subroutine is not found (in the calling module).

I guess that what is happening is that Exporter prepares the list in a BEGIN, when the alias has not been created; but I tried putting the typeglob assignment in a BEGIN block and that didn't help.

I've tried AUTOLOAD, but of course that does not make the name available in the calling context. Of course I could write a series of wrapper functions, but that is tedious. It's possible I could generate wrapper functions automatically, though I'm not sure how.

Any suggestions of a neat way of handling this?

+1  A: 

The following works for me. This seems to be what you're describing; you must have made a mistake somewhere.

Main script:

use strict;
use warnings;
use Bar;

baz();

Module:

package Bar;
use strict;
use warnings;

require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(baz);

sub Baz { print "Baz() here\n" }

*baz = *Baz;

1;
Michael Carman
You're right! It's still not working (in fact in terms of your example, it's saying "Undefined subroutine Bar::baz") but it must be something else. Thanks for the help.
Colin Fine
In fact, it was that there was a second package in the same file (another thing we don't do any more) and the alias (*baz = *Baz) was in the wrong package! Chalk up another reason not to put multiple modules in the same file.
Colin Fine
What you want to say is "make baz a subroutine," but *baz = *Baz is saying "make $baz, @baz, %baz and etc. undefined and $bar = 10; *bar = \ print bar(); print $bar'and:perl -le 'sub foo {42}; $bar = 10; *bar = \*foo; print bar(); print $bar'
converter42
A: 

You must export both names if you want both names to be visible. Using Michael Carman's answer as a base, you need

our @EXPORT = qw(Baz baz);

or

our @EXPORT    = qw(Baz);
our @EXPORT_OK = qw(baz);

if you want to be able to call either one in the program. Just because they point to the same coderef does not mean all names for that coderef will be exported when one is.

Chas. Owens
If I'm reading the original post correctly @EXPORT (i.e. unconditional exporting) is only for backwards compatibility. Presumably users that rely on this are only using the old names. I'm guessing that modules using the new names import them explicitly via either @EXPORT_OK or %EXPORT_TAGS.
Michael Carman
A: 

Exporting

Manually calling @EXPORT =() stuff is getting a bit haggard.

package Bar;
use strict;
use warnings;

use Sub::Exporter -setup => {
    exports => [qw[ foo ]],
    groups  => {
        default => [qw[ foo ]],
    }
};

sub foo(){

};


1;

Use:

use strict;
use warnings;
use Bar  foo => { -as-> 'Foo' };

Sub::Exporter can do lots of awesome stuff, like group exports, group exclusion, builder methods ( Ie: how the subs it exports work are determined by passed parameters , and the subs are generated inside other subs, etc )

Renaming

For renaming things it might be better to have a secondary function which just stands as a legacy function that Carp()s when its called to recommend the code that points to it everywhere to be moved to the new method. This will increase consistency codewide.

Then when your tests stop spouting forth warnings, you can remove the legacy function.

sub old {  # line 1
   Carp::carp('Legacy function \'old\' called, please move to \'newmethod\' '); 
   goto &newmethod; # this passes @_ literally and hides itself from the stack trace. 
} # line 4

sub newmethod { # line 6
   Carp::cluck('In New Method'); 
   return 5;
} # line 9

print old(), "\n";  # line 11
Legacy function 'old' called, please move to 'newmethod' at code.pl line 2
    main::old() called at code.pl line 11
In New Method at code.pl line 7
    main::newmethod() called at code.pl line 11
5

Note how the warnings in newmethod look exactly like they'd been called directly.

Kent Fredric