7

In Perl, is it possible to create a global variable based on a string?

E.g., if I had a function like:

sub create_glob_var {
    my ($glob_var_str) = @_;
    # something like this ( but not a hash access).
    our ${$glob_var_str};
};

and I called it like:

create_glob_var( "bar" );

How could I modify create_glob_var to actually create a global variable called $bar?

My project is using perl 5.8.5.

EDIT

The following doesn't work:

use strict;
BEGIN {
  sub create_glob_var {
    my ($glob_var_str) = @_;
    no strict 'refs';
    $$glob_var_str = undef;  # or whatever you want to set it to
  }

  create_glob_var("bah");
};

$bah = "blah";

Produces:

Variable "$bah" is not imported at /nfs/pdx/home/rbroger1/tmp2.pl line 12.
Global symbol "$bah" requires explicit package name at /nfs/pdx/home/rbroger1/tmp2.pl line 12.
Execution of /nfs/pdx/home/rbroger1/tmp2.pl aborted due to compilation errors.

NOTE I realize that using global variables causes ozone depletion and male pattern baldness. I'm trying to clean up some legacy code that is already completely infected with the use of global variables. One refactor at a time...

Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
Ross Rogers
  • 23,523
  • 27
  • 108
  • 164
  • The subroutine creates the global variable, but you still need to access it in a manner acceptable to strict, probably one of 1) $My::Package::bah = "blah", 2) use vars qw($bah); $bah = "blah", or 3) our $bah = "blah". – Sean Feb 16 '10 at 19:20
  • 4
    Don't do stuff like this. see http://stackoverflow.com/questions/1549685/how-can-i-use-a-variable-as-a-variable-name-in-perl – Sinan Ünür Feb 16 '10 at 19:27
  • Agreed. Use a global hash to store data (if you must use globals at all), rather than creating new scalars! – Ether Feb 16 '10 at 20:32

6 Answers6

5

If you are trying to clean up old code, you can write a module which exports the required variable(s). Every time you feel the need to invoke create_glob_var, instead add a variable to this package and put that in the import list.

This will help you keep track of what is going on and how variables are being used.

package MyVars;

use strict; use warnings;

use Exporter 'import';

our($x, %y, @z);

our @EXPORT_OK = qw( $x %y @z );

The script:

#!/usr/bin/perl

use strict;use warnings;

use MyVars qw( $x %y @z );

$x = 'test';
%y = (a => 1, b => 2);
@z = qw( a b c);

use Data::Dumper;
print Dumper \($x, %y, @z);

Output:

$VAR1 = \'test';
$VAR2 = {
          'a' => 1,
          'b' => 2
        };
$VAR3 = [
          'a',
          'b',
          'c'
        ];
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
3
sub create_glob_var {
    my ($glob_var_str) = @_;
    no strict 'refs';
    $$glob_var_str = undef;  # or whatever you want to set it to
}

The no strict 'refs' is only necessary if use strict is in effect, which it always should be.

Addendum:

If you're asking if there's a way to write a subroutine create_glob_var such that the following code will succeed:

use strict;
create_glob_var("bar");
$bar = "whatever";

...then the answer is "No." However, Perl's vars pragma will do what you want:

use strict;
use vars qw($bar);
$bar = "whatever";

But this is kind of old-style Perl coding. Nowadays, one would typically do this:

use strict;
our $bar = "blah";

our can also just declare global variables that can be freely used later:

our ($foo, @bar, %baz);
# ...
$foo = 5;
@bar = (1, 2, 3);
%baz = (this => 'that');
Sean
  • 29,130
  • 4
  • 80
  • 105
  • Thanks for the answer. It doesn't seem to be working. ``strict`` is complaining at compile(?) time. See my updated post. – Ross Rogers Feb 16 '10 at 19:10
2

You would have to use an eval, but that's generally considered evil. Something like:

eval("$glob_var_str = \@_;");

EDIT

Just verified that you can only do this without the my and with no strict refs.

Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
2

Try looking at this question: Does Perl have PHP-like dynamic variables?

In brief, it seems like you should be able to do $$glob_var_str = "whatever";

Community
  • 1
  • 1
Dan Passaro
  • 4,211
  • 2
  • 29
  • 33
  • You should `no strict refs` in the block first, though. (because you should be using strict!) –  Feb 16 '10 at 18:58
2

The vars pragma already does the heavy lifting for what you want, so put it to work:

#! /usr/bin/perl

use warnings;
use strict;
use vars;

BEGIN { vars->import(qw/ $bah /) }

$bah = "blah";
print $bah, "\n";

If you prefer to spell it create_glob_var, then use

#! /usr/bin/perl

use warnings;
use strict;
use vars;

sub create_glob_var { vars->import("\$$_[0]") }

BEGIN { create_glob_var "bah" }

$bah = "blah";
print $bah, "\n";

Either way, the output is

blah

I'm curious to know why you want to do it this way rather than declaring these variables with our. Yes, it may take a few iterations to catch them all, but these are short-term fixes anyway, right?

In general, you can use a variable as a variable name (see "Symbolic references" in perlref), but you really, really, really don't want to do that: enabling the strict 'refs' pragma disables this feature.

Rafael Garcia-Suarez showed great wisdom when he wrote, “I don't know what your original problem is, but I suggest to use a hash.”

See also:

Greg Bacon
  • 134,834
  • 32
  • 188
  • 245
  • The reason I'm doing all this stuff is someone reinvented the wheel by creating a custom command line parser. It is used throughout all our tool stack. Unfortunately, the current method for declaring an option involves modifying 3 sections of code spread across 3000 lines of code. It is truly horrifying and it violently violates D.R.Y. I'm trying to reduce the need to repeat yourself throughout the entire code base by somehow collecting all the code necessary to create a new option into less than 10 lines of *contiguous* code. – Ross Rogers Feb 17 '10 at 17:14
1

Answer by Sinan Ünür is indeed the best. However, this picked my curiosity, so I did a bit of reading (perldoc perlmod)and learned about "package_name::" hash as a way to access the namespace of a package.

The following code adds a record to symbol table of main:: package:

use strict;
my $name = "blah";
my $var = "sss";
$main::{$name} = \$var;

print "$main::blah\n";

This prints "sss".

However, I had to add package name to print statement because "use strict" is still not fooled. I'll keep looking - use vars does not seem to work at the moment.