Trampoline sub

T

Tim McDaniel

I'm moving some subs from one module to another. I'm thinking of
leaving old names in to give more time to change all the callers.
I think the best way to have "trampoline" code is
sub OldName { goto &NewModule::NewName; }
and it's reasonably clear. Just out of curiosity, are there other
ways?

I tried
*old{CODE} = \ℜ
but it causes
Can't modify glob elem in scalar assignment at local/test/077.pl
line 6, near "ℜ"
Execution of local/test/077.pl aborted due to compilation errors.

I tried
*old = *real;
and it works. Does it have any bad effects, like creating $MAIN::eek:ld
or something?
 
R

Rainer Weikusat

I'm moving some subs from one module to another. I'm thinking of
leaving old names in to give more time to change all the callers.
I think the best way to have "trampoline" code is
sub OldName { goto &NewModule::NewName; }
and it's reasonably clear. Just out of curiosity, are there other
ways?

I would write this as

sub OldName { &NewModule::newname; }

goto &subref is magical in the sense that it is supposed to hide the
fact that some subroutine was created via AUTOLOAD by manipulating the
call stack accordingly. This is probably not necessary in your case.
I tried
*old{CODE} = \ℜ
but it causes
Can't modify glob elem in scalar assignment at local/test/077.pl
line 6, near "ℜ"

The purpose of the *foo{THING} syntax is to access the slots of a
glob, eg

,----
| [rw@sapphire]~ $perl -de 0
|
| Loading DB routines from perl5db.pl version 1.32
| Editor support available.
|
| Enter h or `h h' for help, or `man perldebug' for more help.
|
| main::(-e:1): 0
| DB<1> sub toast { return 'Toast'; }
|
| DB<2> $toast = *toast{CODE}
|
| DB<3> p $toast->()
| Toast
`----

This is not necessary when assigning because a reference of a certain
type is automatically assigned to the correct glob slot,

,----
| [rw@sapphire]~ $perl -de 0
|
| Loading DB routines from perl5db.pl version 1.32
| Editor support available.
|
| Enter h or `h h' for help, or `man perldebug' for more help.
|
| main::(-e:1): 0
| DB<1> sub toast { return 'Toast'; }
|
| DB<2> *food = \&toast
|
| DB<3> p food()
| Toast
`----
Execution of local/test/077.pl aborted due to compilation errors.

I tried
*old = *real;
and it works. Does it have any bad effects, like creating $MAIN::eek:ld
or something?

It does what you were asking for: Put the glob referred to by *real in
the symbol table slot old:

,----
| [rw@sapphire]~ $perl -de 0
|
| Loading DB routines from perl5db.pl version 1.32
| Editor support available.
|
| Enter h or `h h' for help, or `man perldebug' for more help.
|
| main::(-e:1): 0
| DB<1> @real = qw(The rain in Spain stays mainly in the plains)
|
| DB<2> *old = *real
|
| DB<3> p join(' ', @old)
| The rain in Spain stays mainly in the plains
`----
 
C

C.DeRykus

I'm moving some subs from one module to another. I'm thinking of

leaving old names in to give more time to change all the callers.

I think the best way to have "trampoline" code is

sub OldName { goto &NewModule::NewName; }

and it's reasonably clear. Just out of curiosity, are there other

ways?



I tried

*old{CODE} = \&real;

but it causes

Can't modify glob elem in scalar assignment at local/test/077.pl

line 6, near "&real;"

Execution of local/test/077.pl aborted due to compilation errors.



I tried

*old = *real;

and it works. Does it have any bad effects, like creating $MAIN::eek:ld

or something?

One downside would be that all the glob slots of
*old would be aliased to those of *real. Selective
aliasing over a local scope seems preferable:

no warnings 'redefine';
local *old = \&real;
 
R

Rainer Weikusat

C.DeRykus said:
I'm moving some subs from one module to another. I'm thinking of

leaving old names in to give more time to change all the callers.

I think the best way to have "trampoline" code is

sub OldName { goto &NewModule::NewName; }

and it's reasonably clear. Just out of curiosity, are there other

ways?
[...]
I tried

*old = *real;

and it works. Does it have any bad effects, like creating $MAIN::eek:ld
or something?

One downside would be that all the glob slots of
*old would be aliased to those of *real. Selective
aliasing over a local scope seems preferable:

no warnings 'redefine';
local *old = \&real;

If this is supposed to enable calling a subroutine by two different
names, it basically needs to be done in the top-level lexical
scope and can thus as well be done at file scope. And you usage of
'aliasing' is - at best - confusing (I'm tempted to call it 'outright
wrong'). The perl symbol table of some module is a hash named
%module_name:: and creating a 'module scoped' named objects implies
creating a glob, putting that into the symbol table hash indexed by
the name and assigning the object itself to one of the slots of this
glob, cf (the $n = 4 causes the SV slot to become populated and the
later *n = .... the CV slot):

,----
| [rw@sapphire]~ $perl -de 0
|
| Loading DB routines from perl5db.pl version 1.32
| Editor support available.
|
| Enter h or `h h' for help, or `man perldebug' for more help.
|
| main::(-e:1): 0
| DB<1> $n = 4
|
| DB<2> use Devel::peek
|
| DB<3> Dump($::{n})
| SV = PVGV(0x70aa70) at 0xa28df8
| REFCNT = 1
| FLAGS = (MULTI,IN_PAD)
| NAME = "n"
| NAMELEN = 1
| GvSTASH = 0x605bb0 "main"
| GP = 0x70e360
| SV = 0xa28db0
| REFCNT = 1
| IO = 0x0
| FORM = 0x0
| AV = 0x0
| HV = 0x0
| CV = 0x0
| CVGEN = 0x0
| LINE = 2
| FILE = "(eval 5)[/usr/share/perl/5.10/perl5db.pl:638]"
| FLAGS = 0xa
| EGV = 0xa28df8 "n"
|
| DB<4> *n = sub { return 'n'; }
|
| DB<5> Dump($::{n})
| SV = PVGV(0x70aa70) at 0xa28df8
| REFCNT = 1
| FLAGS = (MULTI,ASSUMECV,IN_PAD)
| NAME = "n"
| NAMELEN = 1
| GvSTASH = 0x605bb0 "main"
| GP = 0x70e360
| SV = 0xa28db0
| REFCNT = 1
| IO = 0x0
| FORM = 0x0
| AV = 0x0
| HV = 0x0
| CV = 0xa290b0
| CVGEN = 0x0
| LINE = 2
| FILE = "(eval 5)[/usr/share/perl/5.10/perl5db.pl:638]"
| FLAGS = 0xe
| EGV = 0xa28df8 "n"
|
| DB<6>
`----

In contrast to this, a 'glob to glob' assignment puts the glob
referenced by the name on the RHS into the glob slot of the LHS name:

,----
| [rw@sapphire]~ $perl -de 0
|
| Loading DB routines from perl5db.pl version 1.32
| Editor support available.
|
| Enter h or `h h' for help, or `man perldebug' for more help.
|
| main::(-e:1): 0
| DB<1> use Devel::peek
|
| DB<2> $n = 3;
|
| DB<3> $nn = 33
|
| DB<4> Dump($::{nn})
| SV = PVGV(0x70b400) at 0xa8dd58
| REFCNT = 1
| FLAGS = (MULTI,IN_PAD)
| NAME = "nn"
| NAMELEN = 2
| GvSTASH = 0x605bb0 "main"
| GP = 0xa28580
| SV = 0xa28bd0
| REFCNT = 1
| IO = 0x0
| FORM = 0x0
| AV = 0x0
| HV = 0x0
| CV = 0x0
| CVGEN = 0x0
| LINE = 2
| FILE = "(eval 8)[/usr/share/perl/5.10/perl5db.pl:638]"
| FLAGS = 0xa
| EGV = 0xa8dd58 "nn"
|
| DB<5> *nn = *n
|
| DB<6> Dump($::{nn})
| SV = PVGV(0x70b400) at 0xa8dd58
| REFCNT = 1
| FLAGS = (MULTI,IN_PAD)
| NAME = "nn"
| NAMELEN = 2
| GvSTASH = 0x605bb0 "main"
| GP = 0x70de70
| SV = 0xa026a0
| REFCNT = 2
| IO = 0x0
| FORM = 0x0
| AV = 0x0
| HV = 0x0
| CV = 0x0
| CVGEN = 0x0
| LINE = 2
| FILE = "(eval 7)[/usr/share/perl/5.10/perl5db.pl:638]"
| FLAGS = 0xa
| EGV = 0xa026b8 "n"
`----
 
C

C.DeRykus

C.DeRykus said:
I'm moving some subs from one module to another. I'm thinking of

leaving old names in to give more time to change all the callers.

I think the best way to have "trampoline" code is

sub OldName { goto &NewModule::NewName; }

and it's reasonably clear. Just out of curiosity, are there other

ways?


[...]


I tried

*old = *real;

and it works. Does it have any bad effects, like creating $MAIN::eek:ld
or something?
One downside would be that all the glob slots of
*old would be aliased to those of *real. Selective
aliasing over a local scope seems preferable:

no warnings 'redefine';
local *old = \&real;



If this is supposed to enable calling a subroutine by two different

names, it basically needs to be done in the top-level lexical

scope and can thus as well be done at file scope.

Hm, I'm not following your point. I was just
bringing up that something like:

perl -MMymod=somesub -E
'sub somesub{ say "old"; };
somesub();
{ local *somesub=\&Mymod::somesub; somesub(); };
somesub()'

--> old
new
old

would limit the scope of the new sub to prevent it
from permanently hijacking the original. Even if
this scenario doesn't require it, the narrowest
scope is generally a good idea of course.

'aliasing' is - at best - confusing (I'm tempted to call it 'outright

wrong').
...

A demo from perlmod actually uses the term 'aliasing'
as a cheap, fast way of referencing:

$bar = 1;
*foo = \$bar; # Make $foo an alias for $bar

{
local $bar = 2; # Restrict changes to block
print $foo; # Prints '1'!
}
... Because variables are accessed through the
typeglob, you can use "*foo = *bar" to create
an alias which can be localized...
 
R

Rainer Weikusat

C.DeRykus said:
[...]
Selective aliasing over a local scope seems preferable:
[...]
If this is supposed to enable calling a subroutine by two different
names, it basically needs to be done in the top-level lexical
scope and can thus as well be done at file scope.

Hm, I'm not following your point. I was just
bringing up that something like:

perl -MMymod=somesub -E
'sub somesub{ say "old"; };
somesub();
{ local *somesub=\&Mymod::somesub; somesub(); };
somesub()'

--> old
new
old

would limit the scope of the new sub to prevent it
from permanently hijacking the original.

If, as quoted above, the general idea is to make a 'globally callable
and called' subroutine available via two names for some time, exactly
this 'permanent hijacking' aka 'make the subroutine available via
either name' is intended.

[...]

A demo from perlmod actually uses the term 'aliasing'
as a cheap, fast way of referencing:

$bar = 1;
*foo = \$bar; # Make $foo an alias for $bar

And the examples I gave apply equally well to that: And assignment of
the form

*name = reference

with 'name' being some identifier and reference being a refrence to
some Perl object cause the corresponding slot of the glob referred to
by name to point to the reference. A *glob = *some_other_glob
assignment causes glob and some_other_glob to share the subobject
Devel::peek::Dump refers to a 'GP'. For as long as this happens to be
the case, $bar and $foo and @bar and @foo and &bar and &foo and will
result in the same object. But it doesn't really create a 'special
relationship' between the names bar and foo, cf

perl -e '$bar = 55; *foo = *bar; *bar = *baz; $bar = 66; print "$foo, $bar\n"'

or

perl -e '$bar = 66; *foo = *bar; undef(*bar); print "$foo, $bar\n"'
 
T

Tim McDaniel

Rainer, thank you for the extra explanation.

Indeed, the requirement is that the trampoline/aliasing/name change is
globally visible, to help in changing code that used to call
OldModule::OldSub to NewModule::NewSub anywhere in the code base. So
"local" is not appropriate: if I were going to edit a source file to
add that, I might as well go all the way and do the substitutions in
that source file.

I think all the possibilities are
*OldModule::OldSub = \&NewModule::NewSub;
sub OldModule::OldSub { goto &NewModule::NewSub; }
sub OldModule::OldSub { &NewModule::NewSub; }
sub OldModule::OldSub { NewModule::NewSub(@_); }

On the whole, I think I'll go with Rainer's suggestion of
sub OldSub { &NewModule::NewSub; }

We do get occasional stack dumps that can be exposed to developers.
If I do either of
*OldSub = \&NewModule::NewSub;
or
sub OldSub { goto &NewModule::NewSub; }
then the stack dump would not match the source.
I can imagine someone seeing a stack dump that says that
SomePackage::SomeSub called NewModule::NewSub, looking in
SomePackage.pm for NewSub and not finding it, and getting thoroughly
confused. There are few enough calls to these subs that efficiency
isn't that much of a concern, and I should be able to grep the source
and replace all the existing OldModule::OldSub calls anyway.
 
T

Tim McDaniel

I think all the possibilities are
*OldModule::OldSub = \&NewModule::NewSub;
sub OldModule::OldSub { goto &NewModule::NewSub; }
sub OldModule::OldSub { &NewModule::NewSub; }
sub OldModule::OldSub { NewModule::NewSub(@_); }

and of course
*OldModule::OldSub = *NewModule::NewSub;
 
C

C.DeRykus

and of course

*OldModule::OldSub = *NewModule::NewSub;

Ok, forget 'local' on the latter but since you're
just concerned with the sub, it'd still be better
practice to alias only the CODE slot:


*OldModule::OldSub = \&NewMoldule::NewSub;


Othewise, for instance, an existing SCALAR slot
such as 'OldSub' or 'foo' or whatever the sub's
called could collide.
 
R

Rainer Weikusat

[...]
And you usage of 'aliasing' is - at best - confusing (I'm tempted to
call it 'outright wrong').

Practical example of that: When building state machines in C, I
usually use a function pointer as 'state variable' because this
implies there's no need to explicitly written, state-dependent control
transfer code. The first time I did this in Perl, it occurred to me
that it should be possible to simplify the 'obvious' implementation,
using a module-global scalar variable holding a reference to the
subroutine to be executed next, to one which just invokes the
subroutine by name and change what this name points to. A contrived
example showing my original approach: Assuming the code included below
exists in some location where Perl finds it with the name FlipFlop.pm,

------------
package FlipFlop;

require Exporter;

our @ISA = qw(Exporter);
our @EXPORT = qw(v);

sub v0 {
*v = \&v1;
return 0;
}

*v = \&v0;

sub v1 {
*v = \&v0;
return 1;
}

1;
------------

I expected that using it like this:

------------
use FlipFlop;

while (1) {
print(v(), "\n");
sleep 1;
}
------------

would result in printing a sequence of alternating 0s and 1s, based on
knowing the 'aliasing' statements in perlmod but nothing about the
actual implementation (and without thinking very much about it). This
is, of course, wrong because the assignment done as part of the import
happens once and future changes of the supposedly 'imported' symbol
won't propagate to modules which supposedly 'imported' it.

In hindsight, the documentation on this seems clear enough, but 'in
foresight', it wasn't.
 
C

C.DeRykus

[...]


And you usage of 'aliasing' is - at best - confusing (I'm tempted to
call it 'outright wrong').



Practical example of that: When building state machines in C, I

usually use a function pointer as 'state variable' because this

implies there's no need to explicitly written, state-dependent control

transfer code. The first time I did this in Perl, it occurred to me

that it should be possible to simplify the 'obvious' implementation,

using a module-global scalar variable holding a reference to the

subroutine to be executed next, to one which just invokes the

subroutine by name and change what this name points to. A contrived

example showing my original approach: Assuming the code included below

exists in some location where Perl finds it with the name FlipFlop.pm,



------------

package FlipFlop;



require Exporter;



our @ISA = qw(Exporter);

our @EXPORT = qw(v);
-----
qw(*v); # <----------

Ran into this by accident. Exporting the actual glob
makes your flipflop work:

our @EXPORT = qw( *v );

$ perl -MFlipFlop -le 'while(1) { print v();sleep 1}'
0
1
....


Here's the relevant bit from perlmod:


What makes all of this important is that the
Exporter module uses glob aliasing as the import/
export mechanism. Whether or not you can properly
localize a variable that has been exported from a
module depends on how it was exported:

@EXPORT = qw($FOO); # Usual form, can't be
# localized
@EXPORT = qw(*FOO); # Can be localized


sub v0 {

*v = \&v1;

return 0;

}



*v = \&v0;



sub v1 {

*v = \&v0;

return 1;

}



1;

------------



I expected that using it like this:



------------

use FlipFlop;



while (1) {

print(v(), "\n");

sleep 1;

}

------------



would result in printing a sequence of alternating 0s and 1s, based on

knowing the 'aliasing' statements in perlmod but nothing about the

actual implementation (and without thinking very much about it). This

is, of course, wrong because the assignment done as part of the import

happens once and future changes of the supposedly 'imported' symbol

won't propagate to modules which supposedly 'imported' it.
...

So, evidently, Perl has to be able localize the
glob... to do the glob twiddle on the fly. But
I'm at a loss about the internals.
 
R

Rainer Weikusat

C.DeRykus said:
Rainer Weikusat <[email protected]> writes:
[...]
When building state machines in C, I usually use a function pointer
as 'state variable' because this implies there's no need to
explicitly written, state-dependent control transfer code. The
first time I did this in Perl, it occurred to me that it should be
possible to simplify the 'obvious' implementation,using a
module-global scalar variable holding a reference to the
subroutine to be executed next, to one which just invokes the
subroutine by name
[...]
------------

package FlipFlop;



require Exporter;



our @ISA = qw(Exporter);

our @EXPORT = qw(v);
-----
qw(*v); # <----------

Ran into this by accident. Exporting the actual glob
makes your flipflop work:

our @EXPORT = qw( *v );

$ perl -MFlipFlop -le 'while(1) { print v();sleep 1}'
0
1
...

Here's the relevant bit from perlmod:

What makes all of this important is that the
Exporter module uses glob aliasing as the import/
export mechanism. Whether or not you can properly
localize a variable that has been exported from a
module depends on how it was exported:

@EXPORT = qw($FOO); # Usual form, can't be
# localized
@EXPORT = qw(*FOO); # Can be localized
[...]

So, evidently, Perl has to be able localize the
glob... to do the glob twiddle on the fly.

This is going to become somewhat lengthy ...

What the text you quoted refers to as 'can be localized' is another
side effect of the glob export. I think I should first show the
difference between this "can't be localized" and "can be
localized". Assuming that a file named Localized.pm with the following
content

-----------
package Localized;

require Exporter;

our @ISA = qw(Exporter);
our @EXPORT = qw($a_var a_sub);

our $a_var = 3;

sub a_sub {
return $a_var + 1;
}

1;
------------

is available in the perl module search path, the program included
below

------------
use Localized;

print a_sub(), "\n";

{
local $a_var = 55;

print a_sub(), ' ', $a_var, "\n";
}
------------

will print

,----
| 4
| 4 55
`----

The reason for this can be seen by modifying it as follows:

------------
use Devel::peek;
use Localized;

Dump(*a_var);
Dump(*Localized::a_var);

{
local $a_var = 55;
Dump(*a_var);
}
------------

The output of that is

,----
| SV = PVGV(0x6a3740) at 0x68b558
| REFCNT = 4
| FLAGS = (MULTI,IN_PAD,IMPORT( SV ))
| NAME = "a_var"
| NAMELEN = 5
| GvSTASH = 0x605bb0 "main"
| GP = 0x68e320
| SV = 0x660390
| REFCNT = 1
| IO = 0x0
| FORM = 0x0
| AV = 0x0
| HV = 0x0
| CV = 0x0
| CVGEN = 0x0
| LINE = 193
| FILE = "/usr/share/perl/5.10/Exporter/Heavy.pm"
| FLAGS = 0x1a
| EGV = 0x68b558 "a_var"
| SV = PVGV(0x65b490) at 0x660378
| REFCNT = 3
| FLAGS = (MULTI,IN_PAD)
| NAME = "a_var"
| NAMELEN = 5
| GvSTASH = 0x63cfa8 "Localized"
| GP = 0x62f1d0
| SV = 0x660390
| REFCNT = 1
| IO = 0x0
| FORM = 0x0
| AV = 0x0
| HV = 0x0
| CV = 0x0
| CVGEN = 0x0
| LINE = 8
| FILE = "Localized.pm"
| FLAGS = 0xa
| EGV = 0x660378 "a_var"
| SV = PVGV(0x6a3740) at 0x68b558
| REFCNT = 5
| FLAGS = (MULTI,IN_PAD,IMPORT( SV ))
| NAME = "a_var"
| NAMELEN = 5
| GvSTASH = 0x605bb0 "main"
| GP = 0x68e320
| SV = 0x605d48
| REFCNT = 1
| IO = 0x0
| FORM = 0x0
| AV = 0x0
| HV = 0x0
| CV = 0x0
| CVGEN = 0x0
| LINE = 193
| FILE = "/usr/share/perl/5.10/Exporter/Heavy.pm"
| FLAGS = 0x1a
| EGV = 0x68b558 "a_var"
`----

After the initial import, the GPs of both a_var point to different
objects but the SV slot of each GP points to the same scalar. The
later local changes the binding of the SV slot of the a_var GP in main
but doesn't affect the SV slot of Localized::a_var. When the *a_var
glob is exported instead, this changes to

,----
| SV = PVGV(0x6a3740) at 0x660258
| REFCNT = 4
| FLAGS = (MULTI,IN_PAD,IMPORTALL)
| NAME = "a_var"
| NAMELEN = 5
| GvSTASH = 0x605bb0 "main"
| GP = 0x62f1d0
| SV = 0x660390
| REFCNT = 2
| IO = 0x0
| FORM = 0x0
| AV = 0x0
| HV = 0x0
| CV = 0x0
| CVGEN = 0x0
| LINE = 8
| FILE = "Localized.pm"
| FLAGS = 0xfa
| EGV = 0x660378 "a_var"
| SV = PVGV(0x65b490) at 0x660378
| REFCNT = 3
| FLAGS = (MULTI,IN_PAD)
| NAME = "a_var"
| NAMELEN = 5
| GvSTASH = 0x63cfa8 "Localized"
| GP = 0x62f1d0
| SV = 0x660390
| REFCNT = 2
| IO = 0x0
| FORM = 0x0
| AV = 0x0
| HV = 0x0
| CV = 0x0
| CVGEN = 0x0
| LINE = 8
| FILE = "Localized.pm"
| FLAGS = 0xa
| EGV = 0x660378 "a_var"
| SV = PVGV(0x6a3740) at 0x660258
| REFCNT = 5
| FLAGS = (MULTI,IN_PAD,IMPORTALL)
| NAME = "a_var"
| NAMELEN = 5
| GvSTASH = 0x605bb0 "main"
| GP = 0x62f1d0
| SV = 0x605d48
| REFCNT = 2
| IO = 0x0
| FORM = 0x0
| AV = 0x0
| HV = 0x0
| CV = 0x0
| CVGEN = 0x0
| LINE = 8
| FILE = "Localized.pm"
| FLAGS = 0xfa
| EGV = 0x660378 "a_var"
`----

Here, the GP associated with both names is identical and rebinding the
SV slot of this GP thus causes a change visible to the a_sub
subroutine. With this change, the output of the original program
becomes

,----
| 4
| 56 55
`----

This 'GP' export is also what causes the subroutine switching in the
FlipFlop example to work as intended: Since both FlipFlop::v and
main::v share a GP, changing the CV slot of this GP in FlipFlop is
also visible in main. The downsides of this for a pure subroutine
export/ import are that changes in the importing module may now effect
changes in the exporting module, as demonstrated in the a_var example,
which is not exactly obvious and usually not intended, and that this
still doesn't guarantee that modifications to the FlipFlop symbol
table will remain visible in main: This only works for as long as both
continue to share the GP and it is possible to cause a new GP to be
assigned to either v, eg, by assigning a different glob to *v.
 
C

C.DeRykus

C.DeRykus said:
Rainer Weikusat <[email protected]> writes:


[...]


When building state machines in C, I usually use a function pointer
as 'state variable' because this implies there's no need to
explicitly written, state-dependent control transfer code. The
first time I did this in Perl, it occurred to me that it should be
possible to simplify the 'obvious' implementation,using a
module-global scalar variable holding a reference to the
subroutine to be executed next, to one which just invokes the
subroutine by name


[...]


------------

package FlipFlop;



require Exporter;



our @ISA = qw(Exporter);

our @EXPORT = qw(v);

qw(*v); # <----------
Ran into this by accident. Exporting the actual glob
makes your flipflop work:

our @EXPORT = qw( *v );

$ perl -MFlipFlop -le 'while(1) { print v();sleep 1}'




Here's the relevant bit from perlmod:

What makes all of this important is that the
Exporter module uses glob aliasing as the import/
export mechanism. Whether or not you can properly
localize a variable that has been exported from a
module depends on how it was exported:

@EXPORT = qw($FOO); # Usual form, can't be
# localized
@EXPORT = qw(*FOO); # Can be localized


[...]



So, evidently, Perl has to be able localize the
glob... to do the glob twiddle on the fly.



This is going to become somewhat lengthy ...



What the text you quoted refers to as 'can be localized' is another

side effect of the glob export. I think I should first show the

difference between this "can't be localized" and "can be

localized". Assuming that a file named Localized.pm with the following

content



-----------

package Localized;



require Exporter;



our @ISA = qw(Exporter);

our @EXPORT = qw($a_var a_sub);



our $a_var = 3;



sub a_sub {

return $a_var + 1;

}



1;

------------



is available in the perl module search path, the program included

below



------------

use Localized;



print a_sub(), "\n";



{

local $a_var = 55;



print a_sub(), ' ', $a_var, "\n";

}

------------



will print



,----

| 4

| 4 55

`----



The reason for this can be seen by modifying it as follows:


[ ... ]



Here, the GP associated with both names is identical and rebinding the

SV slot of this GP thus causes a change visible to the a_sub

subroutine. With this change, the output of the original program

becomes



,----

| 4

| 56 55

`----



This 'GP' export is also what causes the subroutine switching in the

FlipFlop example to work as intended: Since both FlipFlop::v and

main::v share a GP, changing the CV slot of this GP in FlipFlop is

also visible in main. The downsides of this for a pure subroutine

export/ import are that changes in the importing module may now effect

changes in the exporting module, as demonstrated in the a_var example,

which is not exactly obvious and usually not intended, and that this

still doesn't guarantee that modifications to the FlipFlop symbol

table will remain visible in main: This only works for as long as both

continue to share the GP and it is possible to cause a new GP to be

assigned to either v, eg, by assigning a different glob to *v.

Good explanation. I'm kinda surprised perlsub
doesn't toss in a quick warning reminder about
the power/danger of typeglob aliasing - esp.
potential damage to encapsulation if there's
a sub call.

Perhaps a more simplistic, yet safer FlipFlop approach would just use 'state':


package FlipFlop;
require Exporter;

our @ISA = qw(Exporter);
our @EXPORT = qw( v );

state $flip = 0;

sub v { $flip =!$flip; return $flip ? 0 : 1}

1;
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,235
Members
46,821
Latest member
AleidaSchi

Latest Threads

Top