Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

undefining one slot of a typeglob

by AidanLee (Chaplain)
on Feb 23, 2004 at 23:49 UTC ( [id://331262]=perlquestion: print w/replies, xml ) Need Help??

AidanLee has asked for the wisdom of the Perl Monks concerning the following question:

Creating and assinging a subroutine reference to a typeglob is quite easy:
*{$name} = sub { return 'piece of cake' };
what I've not been able to figure out is how to remove one:

# error: Can't modify glob elem in scalar assignment 
*{$name}{CODE} = undef;

# error: delete argument is not a HASH or ARRAY element or slice
delete *{$name}{CODE};

# no error, but doesn't work:
delete $My::Package::{$name}{CODE};

what i'm basically looking for is a way to 'undo' changes made to the symbol table. Once i've registered a subroutine in a symbol table, i'd like to be able to unregister it later. There are many examples of the polymorphic assignment abilities of a typeglob in Programming Perl and Damian's OOP book, but nothing I can find there or online addresses removing things from the symbol table.

Replies are listed 'Best First'.
Re: undefining one slot of a typeglob
by chromatic (Archbishop) on Feb 24, 2004 at 00:02 UTC

    Here's some test code I threw together. If you want to preserve the other slots, you'll likely have to use Symbol's gensym to make a new typeglob and assign all of the other slots to it, then use it to replace the old symbol. If you're comfortable with the following code, there you go:

    #!/usr/bin/perl -w use strict; package destination; package main; use Test::More 'no_plan'; { no strict 'refs'; *{'destination::foo'} = sub { 'foo' }; } is( destination::foo(), 'foo', 'install a sub' ); { no strict 'refs'; my $d = *{ 'main::destination::' }; delete $d->{foo}; } ok( ! destination->can( 'foo' ), '... now remove it' );
      Cool, thanks much. I'll look into the Symbol package for my needs. The code you presented is very instructive though. As much as I'm not too concerned about the rest of the glob, i'd rather not risk killing things that i wasn't targeting, just in case it comes back to bite me in the future.
Re: undefining one slot of a typeglob
by dws (Chancellor) on Feb 24, 2004 at 00:19 UTC
    What i'm basically looking for is a way to 'undo' changes made to the symbol table.

    If you make the changes within a scope, you can localize them such that they'll be automagically undone when the scope exits.

    sub foo { print "foo\n" } { local *foo = sub { print "bar\n" }; foo(); } foo(); __END__ bar foo
Re: undefining one slot of a typeglob
by broquaint (Abbot) on Feb 24, 2004 at 00:45 UTC
    Just use undef e.g
    sub foo { print "yep\n"; } @foo = $foo = 'a string'; my $f = 'foo'; foo() if defined &foo; undef &$f; print "nope\n" unless defined &foo; print "\$foo: $foo\n"; print "\@foo: @foo\n"; __output__ yep nope $foo: a string @foo: a string
    HTH

    _________
    broquaint

Re: undefining one slot of a typeglob
by gmpassos (Priest) on Feb 24, 2004 at 05:27 UTC
    I have worked a lot with changes of the symbol table for Safe::World, and looking in the replies that your node got, I think that none of them are the best way.

    Well, say that my way is better is easy, soo I will show what happens in the 3 different types:

  • DELETING FROM THE TABLE:
  • my $d = *{ 'main::destination::' }; delete $d->{foo};
    This will just remove all the entry from the table, but wont ensure that the sub reference was really removed from the memory. Making test of full deletion of packages for Safe::World, is easy to see that if you don't undef explicity a reference from the memory it will stay there even if you clean the HASH of the symbol table.

    But one of the problems here is that you remove all the references, soo, if you have a $foo and &foo, you will erase both.

  • undef
  • Just make a undef to the &sub actually works, but you will still have a *foo{CODE} value in the GLOB, what will use memory.

    Note that undef is the standar way to do that, and is the recomended way, but you won't have the GLOB exactly as you have it before set the sub.

  • Symbol::gensym
  • In Perl is impossible to create a GLOB that exists only in the scope. Soo, gensym() creates a new GLOB at Symbol::GENx, where x is a number. Soo, it won't be cleanned when you go out of the scope. Again you will be just duplicating everything.

  • local()
  • Since a GLOB really need to be in the symbol table, local(*FOO) will create a GLOB that replaces the already existing GLOB in the symbol table for the given scope. Soo, after go out of the scope, the previous will be back. The biggest problem is when you make references to the local(*GLOB) after go out of the scope. Soo, if you make local(*FOO), and want to make something like a closure of it to $holder = \*FOO, it won't work.

    Also for recursion or loop codes, the last local(*FOO) will change the previous sets.

  • NOW MAKEING A UNDO TO A GLOB:
  • Is easy, we just create a holder for the different types inside the GLOB, make a undef, than reset the values, unless the removed type:

    *FOO = sub { print "SUB FOO OK!\n" } ; $FOO = 123 ; print ">>>>>>>>>> BEFORE RM:\n" ; eval{ FOO() } ; warn($@) if $@ ; print "CODE: " . *FOO{CODE} . "\n" ; print "SCALAR: $FOO\n" ; glob_rm( \*FOO , 'CODE' ) ; print ">>>>>>>>>> AFTER RM:\n" ; eval{ FOO() } ; warn($@) if $@ ; print "CODE: " . *FOO{CODE} . "\n" ; print "SCALAR: $FOO\n" ; sub glob_rm { my $glob = shift ; my $type = uc( shift(@_) ) ; my %holder ; foreach my $tp ( qw(SCALAR ARRAY HASH CODE IO FORMAT) ) { $holder{$tp} = *{$glob}{$tp} if $tp ne $type ; } if ($type eq 'SCALAR') { undef ${*{$glob}} ;} elsif ($type eq 'ARRAY') { undef @{*{$glob}} ;} elsif ($type eq 'HASH') { undef %{*{$glob}} ;} elsif ($type eq 'CODE') { undef &{*{$glob}} ;} elsif ($type eq 'IO') { close *{$glob} ;} undef *{$glob} ; foreach my $Key ( keys %holder ) { *{$glob} = $holder{$Key} ;} }
    This will print:
    >>>>>>>>>> BEFORE RM: SUB FOO OK! CODE: CODE(0x1a72bac) SCALAR: 123 >>>>>>>>>> AFTER RM: Undefined subroutine &main::FOO called at tmp.pl line 17. CODE: SCALAR: 123
    Note that this approach is to remove a reference type of a GLOB. If you want to overwrite one of the types, the best is to just set the GLOB with a reference.

    Actually all of this depends of what you need. If you wan't speed, use just undef &FOO, but if you want to save memory I recomend the glob_rm() function.

    Enjoy! ;-P

    Graciliano M. P.
    "Creativity is the expression of the liberty".

      it didn't occur to me that i didn't have to use a typeglob as my holder. good idea, and although it's a core module, this approach means i have one less use statement. Can anybody tell me why do this:
      if ($type eq 'SCALAR') { undef ${*{$glob}} ;} elsif ($type eq 'ARRAY') { undef @{*{$glob}} ;} elsif ($type eq 'HASH') { undef %{*{$glob}} ;} elsif ($type eq 'CODE') { undef &{*{$glob}} ;} elsif ($type eq 'IO') { close *{$glob} ;}
      instead of this:
      ($type eq 'IO') ? close *{$glob} : undef *{$glob}{$type};
      and i have to wonder if just using this would not work:
      undef *{$glob}{$type};
      update: I should note that completely eradicating the sub from memory isn't necessarily the problem here, as sometimes the created CODE glob is simply an alias from another package rather than a brand new anonymous sub.
        If I'm not wrong we can't change a glob key/element, only access it. Soo, this won't work:
        undef *{$glob}{$type} ;
        And I make this:
        if ($type eq 'SCALAR') { undef ${*{$glob}} ;} elsif ($type eq 'ARRAY') { undef @{*{$glob}} ;} elsif ($type eq 'HASH') { undef %{*{$glob}} ;} elsif ($type eq 'CODE') { undef &{*{$glob}} ;} elsif ($type eq 'IO') { close *{$glob} ;}
        Not to remove the element from the GLOB, but to ensure that it goes out of the memory, since undef *{$glob} already remove all the keys/elements. Actually for your needs you can remove all the if ( $type eq '...' ).

        Graciliano M. P.
        "Creativity is the expression of the liberty".

      Symbol::gensym
      In Perl is impossible to create a GLOB that exists only in the scope. Soo, gensym() creates a new GLOB at Symbol::GENx, where x is a number. Soo, it won't be cleanned when you go out of the scope. Again you will be just duplicating everything.
      Symbol::gensym does create its symbols in the Symbol:: package, but then removes them from the package, so when a returned symbol goes out of scope it does get garbage collected. If you think you get a leak doing this, report it as a bug, with a short test case.

      Also, your undefs of each type (and close of the file handle) should never be needed; if you can come up with a short test case that shows a problem, please report this as a perl bug also.

        "Symbol::gensym does create its symbols in the Symbol:: package, but then removes them from the package, so when a returned symbol goes out of scope it does get garbage collected"

        Are you sure about that? I can guarantee to you that it won't make garbage collection! First because we don't have any note about that in the POD of Symbol, and because gensym() just return a reference of a GLOB in the package Symbol::, and any GLOB reference doesn't have scope, since it's a GLOBAL "object". The best way is to see the source of gensym():

        sub gensym () { my $name = "GEN" . $genseq++; my $ref = \*{$genpkg . $name}; delete $$genpkg{$name}; $ref; }

        Soo, when the $ref goes out, the GLOB still exists in the package Symbol::.

        Here's a test of that:

        { my $glob = gensym() ; ${*$glob} = 123 ; print "SCOPED: " . "${*$glob}\n" ; } print "OUT: " . ${*Symbol::GEN0} . "\n";
        And the output:
        SCOPED: 123 OUT: 123

        About the undef of types, well, this was already discussed here in PM, and is all about memory usage. If we make a undef in all the different types of a GLOB to clean a package from the memory, we will free much more memory than just make a undef *GLOB. And the memory difference is big. In my tests after 1000 executions I have the interpreter with 8Mb, and with the simple undef *GLOBS I have 150Mb!

        Graciliano M. P.
        "Creativity is the expression of the liberty".

Re: undefining one slot of a typeglob
by AidanLee (Chaplain) on Feb 24, 2004 at 03:45 UTC
    well, i am not afforded the luxury of limiting the scope of my symbol table manipulations, and for some reason, doing an undef &$foo works in that it destroys the subroutine, but UNIVERSAL::can($package,$method) still returns true, so something wasn't complete there. However, I now have code that does what i need and passes my tests:
    use Symbol qw( gensym ); my $old; { no strict 'refs'; $old = \*{ "package::func_name" }; } my $new = gensym; *$new = *$old{$_} foreach ( grep { defined *$old{$_} } qw( SCALAR ARRA +Y HASH IO FORMAT ) ); { no strict 'refs'; *{ "package::func_name" } = *$new; }

    Edit by tye, change PRE to CODE

      Just a warning: *foo{FORMAT} isn't available before 5.8.0, so the above code will silently remove formats.
        good to know, but i'm honestly not too concerned about it. I included it for completeness, but to be honest i expect most globs going through this process of mine will only have a subref in it anyway.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://331262]
Approved by kvale
Front-paged by thelenm
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (1)
As of 2024-04-24 16:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found