http://qs1969.pair.com?node_id=540262

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

A coworker and I are looking at an inheritance problem here at work. We have a particular class used for settings. For our parent class, we have several package variables that correspond to global settings. Children modules would then add specific settings for their instances.

The problem comes into play when we want to access the global or common settings. The functionality we would like is that of C++ static variables where any class can change the variable and it is visible to all.

Is there some way to mimic this functionality in perl?

To be specific, suppose we have a parent class A with setting "knob1". We would access this normally with:

$A::knob1

If we have a module B that inherits from A, we would like to be able to change knob1 in the following way:

$B::knob1 = 0;

And have $A::knob1 be automatically updated.

Then we get even more complex with another child of A, say, module C. Module B and Module C are at the same level and share module A as an ancestor. But a change in module C to a globally (inherited) setting should be visible in module B.

Any ideas?

Replies are listed 'Best First'.
Re: Static Variables in Perl
by friedo (Prior) on Mar 30, 2006 at 20:24 UTC
    The best way to do this is probably to just inherit accessor and mutator methods, and not try to alter the class data directly.

    package Parent; our $knob = 1; sub knob { my $class = shift; $knob = $_[0] if @_; return $knob; } package Child; use base 'Parent'; # inherits knob method

    That way, calling Parent->knob or Child->knob will get and set the same package global in Parent. To make it safer, you could make $knob a lexical, so it's only visible via the method call.

    Update: Better mutator code per Tanktalus's comment.

      Minor tweak:

      $knob = $_[0] if @_;
      That way, Parent->knob() is an accessor, while Parent->knob(undef) is a mutator that sets the $knob to undef. Slightly better:
      if (@_) { # check that $_[0] is allowed. $knob = $_[0]; }
      but that's only if there are invalid values you want to block.

Re: Static Variables in Perl
by herveus (Prior) on Mar 30, 2006 at 20:51 UTC
    Howdy!

    ...or, you could do something like the following:

    package A; use vars qw/$knob1/; package B; @ISA=('A'); *knob1 = *A::knob1; package main; $A::knob1 = 'a knob1'; $B::knob1 = 'b knob1'; print $A::knob1, "\n"; print \$A::knob1, "\n"; print \$B::knob1, "\n";
    which printed:
    b knob1 SCALAR(0x10062530) SCALAR(0x10065230)
    showing that the two variables are each a name for the same location.

    yours,
    Michael

      Did a double-take there... 'SCALAR(0x10062530)' ne 'SCALAR(0x10065230)'. But I assume that's a typo, since it works for me:

      b knob1 SCALAR(0x8145c50) SCALAR(0x8145c50)

Re: Static Variables in Perl
by Anonymous Monk on Mar 30, 2006 at 23:03 UTC
    You can use Class::MethodMakers -static option.
    perl -w package A; use Class::MethodMaker [ scalar => [{ -static => 1 }, 'knob1'], new => 'new']; package B; @ISA=('A'); $a=new A; $a->knob1(5); $b=new B; print $b->knob1; __END__ 5
Re: Static Variables in Perl
by Herkum (Parson) on Mar 31, 2006 at 00:35 UTC

    The key to a problem is identifying the real issue, the issue you are really having is that you want to abstract access of a variable in a particular namespace. But you want to ignore which namespace it is in. The thing about variables, they are bound to a particular namespace, you cannot have a variable be, $B::Knob1 and $A::Knob1.

    So you have to write a method if you want to have one variable with everything being able to access that value from multiple namespaces.

Re: Static Variables in Perl
by benizi (Hermit) on Mar 31, 2006 at 22:36 UTC

    How's this?

    $ head -n 1 *.pm ==> AA.pm <== package AA; use base qw/Exporter/; our @EXPORT=qw/$knob1/; our $knob1 += 'no'; 1 ==> BB.pm <== package BB; use AA; our @ISA=qw/AA/; our @EXPORT=qw/$knob1/; 1 ==> CC.pm <== package CC; use BB; our @ISA=qw/BB/; our @EXPORT=qw/$knob1/; 1 $ perl -MCC -MBB -lwe '$BB::knob1=42; print for $AA::knob1, $BB::knob1 +, $CC::knob1' 42 42 42

    Note, however, that the following *doesn't* work. (use base doesn't call import)

    $ head -n 1 *.pm ==> AA.pm <== package AA; use base qw/Exporter/; our @EXPORT=qw/$knob1/; our $knob1 += 'no'; 1 ==> BB.pm <== package BB; use base qw/AA/; our @EXPORT=qw/$knob1/; 1 ==> CC.pm <== package CC; use base qw/BB/; our @EXPORT=qw/$knob1/; 1 $ perl -MCC -MBB -lwe '$BB::knob1=42; print for $AA::knob1, $BB::knob1 +, $CC::knob1' no 42 Use of uninitialized value in print at -e line 1.