Using plain package variables is a way to gain global variables, because they're accessible program-wide:
That is, if we fully qualify a variable, including its package, we're always allowed to access it. And yes, this works under strict too. OTOH, this approach is pretty weak with respect to typos.package A; print "the global k in package B is $B::k\n";
The fact is that this really sucks. We're all lazy, and we don't want all that typing. One interesting suggestion is to code a sub, that can be easily made really global, so that we can use it wherever we need (see here):
This really scares me a bit. This is really globally pervasive, and I fear (but it's my ignorance, for sure) that it could break when we're using some modules. For example: what happens when we use AUTOLOAD too? I really don't know, but I suspect that half of the audience won't be happy. Moreover, this approach is subject to typos as well, because the compiler will accept a call to a function pretty happily.package main; my $MW = ...; # Create your main window here. sub UNIVERSAL::MW {$MW}
That's why I thought about creating something that could be used like this (from the SYNOPSIS of vars::global):
The rationales are the following:# In the place/package where we want to create globals use vars::global create => qw( $foo @bar %baz ); # Add some more global symbols vars::global->create(qw( $hello @world %now )); # Somewhere else, where we need to access those globals use vars::global qw( $foo @bar %baz ); # Don't try to use globals that do not exist use vars::global qw( $Foo ); # typo, croaks use vars::global qw( @inexistent ); # we don't create by default # use 'create' as above # You can also import and create new globals use vars::global qw( $foo %baz ), create => qw( $hey @joe ); # If you're lazy, you can import all the globals defined so far use vars::global ':all';
The "core" of the import is the following line:package vars::global; { use version; $VERSION = qv('0.0.1'); use warnings; use strict; use Carp; # Module implementation here # Where we keep existing globals my %ref_for; sub import { my $package = shift; my $caller = caller; my @import; GRAB_IMPORTS: while (@_) { my $param = shift; last GRAB_IMPORTS if lc($param) eq 'create'; if (lc($param) eq ':all') { @import = keys %ref_for; while (@_) { last GRAB_IMPORTS if shift eq 'create'; } last GRAB_IMPORTS; } push @import, $param; } $package->_create($caller, @_); $package->_import($caller, @import, @_); return; } ## end sub import sub create { my $package = shift; my $caller = caller; $package->_create($caller, @_); $package->_import($caller, @_); return; } ## end sub create sub has { my $package = shift; my ($symbol) = @_; return unless exists $ref_for{$symbol}; return $ref_for{$symbol}; } ## end sub has sub _create { my $package = shift; my $caller = shift; my @symbols = @_; no strict 'refs'; no warnings 'once'; foreach my $symbol (@symbols) { # Some checks croak "undefined symbol" unless defined $symbol; croak "empty symbol" unless length $symbol; my $identifier = substr $symbol, 1; croak "invalid identifier '$identifier'" unless $identifier =~ /\A \w+ \z/mxs; my $fqn = $package . '::' . $identifier; my $sigil = substr $symbol, 0, 1; $ref_for{$symbol} = $sigil eq '$' ? \${$fqn} : $sigil eq '@' ? \@{$fqn} : $sigil eq '%' ? \%{$fqn} : croak "invalid sigil: '$sigil'"; } ## end foreach my $symbol (@symbols) return; } ## end sub _create sub _import { my $package = shift; my $caller = shift; no strict 'refs'; foreach my $symbol (@_) { my $ref = $package->has($symbol) or croak "non existent global: '$symbol'"; *{$caller . '::' . substr $symbol, 1} = $ref; } return; } ## end sub _import } 1; # Magic true value required at end of module __END__
which strips the sigil out of $symbol to get the variable identifier, prepends the package name (in $caller) with the colons and defines the symbol in the symbol table. Something basically equal to what happens in vars itself, but I have to credit MJD for having learnt it (e.g. here).*{$caller . '::' . substr $symbol, 1} = $ref;
We keep a hash %ref_for to remember which global variables have been created so far. New items are added only when the create() method is called, either explicitly or using the create parameter in the import list. For this reason, the import() method first selects the symbols that have to be simply imported from those that require creation too, so that you can write something like:
One can check if a symbol already exists with the has() method, which returns a reference to the global variable (if this exists) or undef.use vars::global qw( $these @exists ), create => qw( $those @are %new );
The two methods _import() and _create() factor out the code, so that we can call them from both import() and create(). They cannot call each other, because otherwise caller would be spoiled by the added call. Instead, we factor the code out, call caller from the "public" method (that without the underscore) and then pass this as a parameter to the "private" one (that with the underscore).
Conclusion: I had fun writing it... but shall I ever use it? I doubt :)
Flavio
perl -ple'$_=reverse' <<<ti.xittelop@oivalf
In reply to Global variables by polettix
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |