Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Can you create *real* global variables?

by broquaint (Abbot)
on Jan 25, 2002 at 01:00 UTC ( [id://141331]=perlquestion: print w/replies, xml ) Need Help??

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

The Question
At some point in the past couple of days of hacking and pondering perl I came across an interesting quandry - is it possible to create a truly *global* variable? While I have no intention of actually doing so I wonder if it's possible.

What I/we know
I'm aware of the difference between package (sometimes referred to as global) and lexical variables and know they can both only be seen from their respective scopes. There are, however, global magic variables which can be seen from *everywhere*. For example ...

The Code

{ package Foo; # now lives in Foo's symbol table $pkg_var = "a string"; use strict; # only lives until the end of the block my $lex_var = "another string"; # set the global var $! (which is why we don't die) open(FH, "non_existant_file"); 1; } package main; print "package var - $Foo::pkg_var\n"; print "lexical var - $lex_var\n"; print "global var - $!\n";
Thoughts so far
The only global variables that I know of are the one's created by perl, and they tend to be magical (e.g $! yields the current value of errno when in a numerical context, and an error message in a string context). So I'm guessing you can probably roll your own global using some XS voodoo, or some other magical method. I'm almost certain that it's possible as there seems to be very little in perl these days that can't be done (just look at what TheDamian has been producing recently). I also hope that it's not easily possible, as mis-used package variables are bad enough (which why we use strict :o)
Thanks in advance!

broquaint

footnote: yes I stole this question format from larryk, but only because it's so darn cool!

Replies are listed 'Best First'.
Re (tilly) 1: Can you create *real* global variables?
by tilly (Archbishop) on Jan 25, 2002 at 02:09 UTC
    There are 2 ways I have seen to do this. One, 5.6 and higher, is to start the name with a ^. Blame Dominus for this hack. The other one is to start the name with a '.

    In both cases the variable is always kept in package main. And both syntaxes will not be supported in Perl 6.

      Except that starting a variable name with a ' is just a hack for starting it with a ::, which doesn't stop me from then appending a different namespace. The ^ hack is totally separate.

      _____________________________________________________
      Jeff[japhy]Pinyan: Perl, regex, and perl hacker.
      s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

        I just said what would work, not why it does.

        You might also have noted that I did not say why each solution was going to not be supported in 6.0. I have to leave people some incentive to re-read the documentation! :-)

Re: Can you create *real* global variables?
by Dominus (Parson) on Jan 25, 2002 at 18:55 UTC
    You may want to read the section at the very end of perlvar titled Technical Note on the Syntax of Variable Names.

    As tilly pointed out, variables such as ${^Foo} and @{^_I_Like_Pie} are not only global, but also ignore package declarations, and are the same in every package. ${^Foo} means $main::^Foo, regardless of what package declaration is in scope. But note also that they are all reserved for future extensions, except the ones beginning with ^_. So the official answer to your question is either

    Write the empty package qualifier explicitly, as $::foo or $'foo
    or
    Use a variable like ${^_foo}
    That's two ways to accomplish the same effect, and should be enough to solve any practical problem you might have.

    As the man page mentions, there are a few other variable names that are interpreted independent of the current package: %ENV, STDIN, @ARGV, and so on. There is absolutely no way to create new special variables of this type without modifying the Perl core. (See the code in the vicinity of line 588 of gv.c to see why.) But if you really want to, you can take over the unused variables with the same names, such as $ENV, @STDIN, and %ARGV, and use those; they get the same special treatment even though Perl doesn't use them for anything. Similarly, punctuational variables such as @* and %: ignore the current package declaration and are always taken to be in package main.

    Hope this all helps.

    --
    Mark Dominus
    Perl Paraphernalia

Re: Can you create *real* global variables?
by wog (Curate) on Jan 25, 2002 at 03:08 UTC
    Sounds like an interesting problem... If you don't use strict you can probably do the equivilent of importing the var from the main package into all subpackages of main that exist at that point in time. You could probably do this with an INIT block that inspects and modifies the symbol table. This, however, won't work for code that runs at compile-time. It also won't work for packages created at run time.

    BEGIN { %main::_shared_vars = ( the_very_global => \$the_very_global ); } INIT { package main; sub _iterate_packages_loop { my($stash,$name,$callback) = @_; $callback->($stash,$name); foreach (keys %{$stash}) { if (/::\z/ and $_ ne "main::") { _iterate_packages_loop($stash->{$_},$name.$_,$callback); } } }; sub _iterate_packages { _iterate_packages_loop(\%::,"main::",shift); } _iterate_packages(sub { $_[1] eq "main::" and return; while (my($k,$v) = each %::_shared_vars) { $_[0]->{$k} = $v; } }); } $the_very_global = "GLOBAL!\n"; print $the_very_global; package bar; print $the_very_global; package bar::foo; print $the_very_global;

    Fixing this technique to work with use strict can be done by overriding the strict pragma. One can even override it such that strict works as a source-filter that detects package usage and imports the variables appropriately before the code using them is compiled, so most of the advantage of using strict is not lost. I also use caller so if one does package Foo; use strict; it will still work. use strict; package Foo; (on one line) is not likely to work, however.

    BEGIN { %main::_shared_vars = ( the_very_global => \$the_very_global ); } BEGIN { use Filter::Util::Call; use strict; my $_strict_import = \&strict::import; package strict; sub _add_to_stash { my $name = shift; no strict 'refs'; while (my($k,$v) = each %::_shared_vars) { # importing this way makes the variable # be considered "declared" for the purposes # of strict. *{$name."::$k"} = $v; } }; *strict::import = sub { # setup the package we are being called by _add_to_stash(scalar caller); filter_add(sub { my $status; while (($status = filter_read()) > 0) { _add_to_stash($1) while /package\s+([\w:]+)/g; last unless(/package\s*\z/); } return $status; }); goto &$_strict_import; }; }

    Note that this code doesn't do very smart parsing of package names. I think that's okay since getting false positives is not really a problem. A combination of these two strategys should work except for compile-time run code that doesn't use strict and run-time generated packages that don't use strict. Note that the strategy used with strict can be applied to any module.

    There are probably other ways to do this too. The debugging mechanisms may be able to obtain the necessary information to check for package declarations in files after they are loaded and before they are compiled. Alternately, one could write a source filter that does good parsing of perl (Text::Balanced would be helpful) and find all the package declarations and recode requires, dos, evals, uses etc. into something that would also check for package statements appropriately. (The filter would presumably be setup with the main program and work it's way from there.) This would probably be much, much harder than any of the methods mentioned here, though it would be more effective.

Re: Can you create *real* global variables?
by Ido (Hermit) on Jan 25, 2002 at 01:11 UTC
    The special vars of Perl aren't REAL global. They simply live in the main:: package. The magic is that when you write $! it's enforced to the main package...
Re: Can you create *real* global variables?
by robin (Chaplain) on Jan 25, 2002 at 23:50 UTC
    Something that hasn't being mentioned is that all punctuation variables are always global, even if they aren't magical. It can be hard to find unused punctuation characters though!
    %% = (foo => 23); package xxx; print $%{foo};
    I dimly remember that in Perl 4 any variable or function starting with an underscore was always global; but that changed when Perl 5 came along.

    The situation with ${^Name} variables is a little more complicated: the fact is that any variable name that starts with a punctuation character is implicitly global, but the Perl parser only recognises single punctuation characters. There's one exception to that: you can also use :: as a name, so you can use the variable $:: which is of course global. (And no, it's not a package qualifier - the variable really is called ::. The reason it's allowed is because the root symbol table hash is called %::, so the variable name tokeniser needs to let it through.) But underneath, any string of characters which begins with punctuation will force globality:

    ${'@$??!'} = "Weird!\n"; package SomewhereElse; print ${'@$??!'}
    What's special about ${^Thing} is that (in recent releases) the parser is able to parse the name directly, and it converts the first character into a control char! So the following will work, because control-H is the backspace character:
    ${^Hello} = "Curious...\n"; print ${"\bello"};
    Some food for obfu, maybe... :)
      Said robin:
      Something that hasn't being mentioned is that all punctuation variables are always global, even if they aren't magical.
      I did say this.

      The situation with ${^Name} variables is a little more complicated: the fact is that any variable name that starts with a punctuation character is implicitly global, but the Perl parser only recognises single punctuation characters.
      The actual rule is that any name that starts with a character other than a letter or underscore is global. (Unless your source code is written in unicode, in which case i don't know what happens.) So in particular, $0 and $1 are global. Variables like ${^Name} don't start with punctuation either: As robin said, this one appears to start with ^, but actually it starts with the control-N character. Since control-N isn't a letter or underscore, ${^Name} is global.

      There's one exception to that: you can also use :: as a name, so you can use the variable $:: which is of course global.
      Not so. $:: is actually the same variable as $main::main::. (Thanks to Abigail for tracking this down.) There's a special case in gv.c that treats an empty package name as main, and it gets invoked twice here.

      --
      Mark Dominus
      Perl Paraphernalia

Re: Can you create *real* global variables?
by moodster (Hermit) on Jan 25, 2002 at 19:01 UTC
    The only global variables that I know of are the one's created by perl, and they tend to be magical (e.g $! yields the current value of errno when in a numerical context, and an error message in a string context).
    I'm having a cold and my brain is only operating at half speed right now, so maybe I'm just confused. But I always thought that variables lived either in scalar or list context. Are there such beasts as numerical and string contexts as well?

    Cheers,
    -- moodster

      I'm having a cold and my brain is only operating at half speed right now, so maybe I'm just confused. But I always thought that variables lived either in scalar or list context. Are there such beasts as numerical and string contexts as well?

      Indeed in Perl 5 (and earlier), as opposed to Perl 6, there is no precise notion of numerical or string contexts. However those terms are sometimes loosely used to indicate "used as a number" and "used as a string respectively": in some cases, but by no means in all of them, there's an actual difference.

A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (9)
As of 2024-03-28 10:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found