Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Trampoline sub

38 views
Skip to first unread message

Tim McDaniel

unread,
Nov 2, 2012, 5:51:01 PM11/2/12
to
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::old
or something?

--
Tim McDaniel, tm...@panix.com

Rainer Weikusat

unread,
Nov 2, 2012, 6:25:35 PM11/2/12
to
tm...@panix.com (Tim McDaniel) writes:
> 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::old
> 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.DeRykus

unread,
Nov 3, 2012, 3:15:32 AM11/3/12
to tm...@panix.com
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;



--
Charles DeRykus

Rainer Weikusat

unread,
Nov 3, 2012, 2:53:42 PM11/3/12
to
"C.DeRykus" <der...@gmail.com> writes:
> On Friday, November 2, 2012 2:51:01 PM UTC-7, Tim McDaniel wrote:
>> 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::old
>> 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.DeRykus

unread,
Nov 4, 2012, 10:27:10 AM11/4/12
to
On Saturday, November 3, 2012 11:53:44 AM UTC-7, Rainer Weikusat wrote:
> "C.DeRykus" <der...@gmail.com> writes:
>
> > On Friday, November 2, 2012 2:51:01 PM UTC-7, Tim McDaniel wrote:
>
> >> 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::old
>
> >> 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...

--
Charles DeRykus

Rainer Weikusat

unread,
Nov 4, 2012, 11:58:13 AM11/4/12
to
"C.DeRykus" <der...@gmail.com> writes:
> On Saturday, November 3, 2012 11:53:44 AM UTC-7, Rainer Weikusat wrote:
>> > On Friday, November 2, 2012 2:51:01 PM UTC-7, Tim McDaniel wrote:
>> >> 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.

[...]

>> 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.

[...]


>> '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

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"'

Tim McDaniel

unread,
Nov 4, 2012, 1:59:08 PM11/4/12
to
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.

--
Tim McDaniel, tm...@panix.com

Tim McDaniel

unread,
Nov 4, 2012, 2:05:58 PM11/4/12
to
In article <k76dts$pu9$1...@reader1.panix.com>,
Tim McDaniel <tm...@panix.com> wrote:
>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;

--
Tim McDaniel, tm...@panix.com

C.DeRykus

unread,
Nov 4, 2012, 6:22:39 PM11/4/12
to
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.


--
Charles DeRykus

Rainer Weikusat

unread,
Nov 5, 2012, 9:45:43 AM11/5/12
to
Rainer Weikusat <rwei...@mssgmbh.com> writes:

[...]

> 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.DeRykus

unread,
Nov 5, 2012, 3:11:32 PM11/5/12
to
On Monday, November 5, 2012 6:45:45 AM UTC-8, Rainer Weikusat wrote:
> Rainer Weikusat <rwei...@mssgmbh.com> writes:
>
>
>
> [...]
>
>
>
> > 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.


--
Charles DeRykus

Rainer Weikusat

unread,
Nov 7, 2012, 12:37:42 PM11/7/12
to
"C.DeRykus" <der...@gmail.com> writes:
> On Monday, November 5, 2012 6:45:45 AM UTC-8, Rainer Weikusat wrote:
>> Rainer Weikusat <rwei...@mssgmbh.com> 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.DeRykus

unread,
Nov 8, 2012, 2:31:07 PM11/8/12
to
[ ... ]
>
>
>
> 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;

--
Charles DeRykus
0 new messages