Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Accessing hash from within module

by hmonroe (Novice)
on Feb 11, 2012 at 22:39 UTC ( [id://953265]=perlquestion: print w/replies, xml ) Need Help??

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

I would like to be able to access a hash in the main module as if it was in a submodule. As background, I have a 20,000 line program I am trying to refactor into modules. For the key variables (this is a text editor), I can put "my $textwindow=$main::textwindow" at the top of a subroutine in the module. However, I do not see how to do this with a hash. The problem is that someone put most of the variables in the main module into a hash %lglobal. Therefore I do not want to put "$main::lglobal{xxx}" every time I refer to the variable lglobal{xxx}. This makes the code hard to read.

Replies are listed 'Best First'.
Re: Accessing hash from within module
by Jenda (Abbot) on Feb 11, 2012 at 23:20 UTC

    Either once execute

    *lglobal = \%main::lglobal; # %lglobal in current package is an allias + to %main::lglobal

    or see Data::Alias

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.

Re: Accessing hash from within module
by BrowserUk (Patriarch) on Feb 12, 2012 at 01:12 UTC

    As an extension of Jenda's post. Instead of making the entire %lglobal hash visible, just cherry pick the bits of it each module needs:

    { our( $xxx, @yyy ); *xxx = \$main::lglobal{ xxx }; # $xxx is now an alias for $main::lglobal{ xxx }; *yyy = \@{ $main::lglobal{ yyy } }; # @yyy is now an alias for the array ref in $main::lglobal{ yyy }; # meaning instead of having to write: for my $i ( 0 .. $#{ $main::lglobal{ yyy } } ) { $main::lglobal{ yyy }[ $i ] = 123; } # you can just write: for my $i ( 0 .. $#yyy ) { $yyy[ $i ] = 123; } }

    And you can scope the visibility to exactly the packages, subroutines or just blocks where you need it, giving effectively lexically scoped access to your global data. It's one of Perl's most underrated features.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

Re: Accessing hash from within module
by bart (Canon) on Feb 12, 2012 at 02:07 UTC
    You might think of importing the variable into your main package, if it is defined in a module.

    That's actually do the same thing as Jenda's code snippets, but it looks just a bit less hacky. See Exporter for one way to implement it.

    You could also think of declaring the global variables in a dedicated module and use it in any module that requires access to them.

    Also note that you can use "::" as short for "main::". So you could just spell it as %::lglobal (and $::lglobal{$key}), which doesn't look to bad to me. You could event think of the "::" prefix as hungarian notation, so it could even work out for the better.

Re: Accessing hash from within module
by Xiong (Hermit) on Feb 12, 2012 at 07:18 UTC

    There are many workable approaches and you've already seen several; I'd like to offer another. I hesitate to call it a true object-oriented approach. What I'm doing here is suborning OO techniques to manage what I call a pseudo-global football. This is almost as evil as global variables; just a bit less so.

    Code such as $f->{barky} is only marginally more complex than a lexical $barky (and it should be, at least a little, since the football goes all over the field). Besides new() and init() you can write additional methods that deal primarily with the football; with the extra payoff that the calling convention is quite clean. Otherwise, you can pass the football around to almost anything, almost anywhere:

    do_stuff({ -more_important_stuff => $my_stuff, -football => $f, });

    In the following demo, I've put the OO::Module class in the same file as the main script. Don't do that in real life; make a real module and put it in /lib. While you're at it, change identifiers to match your project's needs.

    #!/usr/bin/perl # football.pl # = Copyright 2012 Xiong Changnian <xiong@cpan.org> = # = Free Software = Artistic License 2.0 = NO WARRANTY = use 5.014002; use strict; use warnings; use lib qw| lib |; use Devel::Comments '###', '####'; #~ use Devel::Comments '#####', ({ -file => 'debug.log' }); #--------------------------------------------------------------------- +-------# # Get a new football. my $f = OO::Module->new; # Call OO method on football. $f->_method({ fruit => 'apple', name => 'Fuji', }); # Access football, bypassing class package. if ( $f->{oddball} ) { say $f->{apple} }; # Dump entire football for debugging. ### $f #===================================================================== +=======# package OO::Module; use strict; use warnings; #--------------------------------------------------------------------- +-------# #=========# CLASS METHOD # # my $obj = $class->new(); # my $obj = $class->new({ -a => 'x' }); # # Purpose : Object constructor # Parms : $class : Any subclass of this class # anything else will be passed to init() # Returns : $self # Invokes : init() # # Good old-fashioned hashref-based object constructor. # sub new { my $class = shift; my $self = {}; # always hashref bless ($self => $class); $self->init(@_); # init remaining args return $self; }; ## new #=========# OBJECT METHOD # # $obj->init({ k => 'v', f => $b }); # # Purpose : Initialize hashref-based object. # Parms : $self # : $hashref : arbitrary key/value pairs # Returns : $self # Throws : dies if $hashref can't be dereferenced to a hash # See also : new() # sub init { my $self = shift; my $hashref = shift // {}; # Do any pre-initializing of defaults here... $self->{oddball} = 1; # Merge all values. Newer values always overwrite. %{$self} = ( %{$self}, %{$hashref} ); return $self; }; ## init #=========# OBJECT METHOD # # $obj->_method({ '-parm' => $value, }); # short # # Purpose : ____ # Parms : ____ # Reads : ____ # Returns : ____ # Invokes : ____ # Writes : ____ # Throws : ____ # See also : ____ # # ____ # sub _method { my $self = shift; my $argref = shift; # Access pseudo-global football. $self->{ $argref->{fruit} } = $argref->{name}; return $self; }; ## _method __END__
    Output:
    Fuji ### $f: bless( { ### apple => 'Fuji', ### oddball => 1 ### }, 'OO::Module' )
    I'm not the guy you kill, I'm the guy you buy. —Michael Clayton
      This is almost as evil as global variables; just a bit less so.

      The real trouble with this OO route -- besides that it derives no true benefit from the costs of the OO -- is the clumsy syntax.

      Somewhere in your OO-football wrapped global, there is an array that needs to be iterated and its contents accessed:

      my $o = OO::Module->new; for my $i ( 0 .. $o->yyy->highest_index ) { $o->yyy->set_element( $i, $o->yyy->get_element( $i ) + 1 ); }

      For contrast, compare with:

      our @yyy; *yyy = \@{ $main::lglobal{ yyy } }; ++$yyy[ $_ ] for 0 .. $#yyy

      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice. -- The problem with OO; is not the OO, but ooholes that use it.

      The start of some sanity?

Re: Accessing hash from within module
by flexvault (Monsignor) on Feb 12, 2012 at 09:40 UTC

    hmonroe,

    I have a 20,000+ line package with about 6 modules, and I use 'our' in parent that forks copies of itself once all global data has been initialized. I'm sure this is just another way of looking at the problem, but it does work for forked copies, so I'm sure it will work for all modules sharing one address space. The sample code is just a skeleton.

    parent code:

    { ## Parent our ( %hash, @array, $scalar, $LOCK ); ## Global data my $lockfile = "./GobalLock"; open ( $LOCK, ">>", $lockfile ) or die "Can't open $lockfile; $!\n"; ... # code to fork and maintain number of children }

    If your package is running as a single user, you don't have to lock the parent globals, but you must lock them if more than one module needs access to the global data. When the children want to get or update global data:

    { my $need; ... if ( flock( $LOCK, LOCK_SH ) ) ## Get a copy of Global data { $need = $hash{"stuff"}; flock( $LOCK, LOCK_UN ); } else { die "$!\n"; } ... if ( flock( $LOCK, LOCK_EX ) ) ## Update the Global data { $hash{"stuff"} = $need; flock( $LOCK, LOCK_UN ); } else { die "$!\n"; } ... }

    If you scope each module with a separate pair of brackets {}, and you are of course running with 'strict' and 'warnings', sometimes I get an error that I need to define a global. I just add the 'our ...' after the top '{' and it covers the entire module. It doesn't seem to happen as much as in the past, but if you see it just add the our in scope.

    Good Luck!

    "Well done is better than well said." - Benjamin Franklin

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2024-03-29 13:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found