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

Hello monks,

I seek your wisdom in determining the best way to access a package variable of a subclass from within a base class method. I have identified a working solution, but it is ugly and I don't completely understand it. It also uses stringy eval, which I know to be taboo in most cases.

Surely there must be a more elegant (or correct) way for a method in a base class to access the package variables of the subclass that inherited it?

Thanks in advance, and here is some functional example code illustrating what I am trying to accomplish.

m.

#================= package BaseClass; #================= our $ID = 1; sub new { my $class = shift; return bless {}, $class; } sub id { my $self = shift; my $class = ref $self; return eval "\$${class}::ID"; } #================ package SubClass; #================ use base qw( BaseClass ); our $ID = 2; #============ package main; #============ my $obj = new SubClass; my $id = $obj->id(); print "$id\n";

Replies are listed 'Best First'.
Re: Access package variables of subclass from base class
by moritz (Cardinal) on Mar 04, 2009 at 08:24 UTC
    Write a an accessor in each child class, like this:
    #!/usr/bin/perl use strict; use warnings; { package Parent; sub f { my $self = shift; $self->classvar += 5; } } { package Child; our @ISA = qw(Parent); our $classvar = 3; sub classvar :lvalue { $classvar; } } my $obj = bless {}, 'Child'; $obj->f(); print $Child::classvar, $/; # output: 8

    Here the method f in the parent class accesses the method classvar in the child.

    If you only want to allow read access, you can leave out the :lvalue attribute.

      Thanks moritz, this looks like the cleanest solution. I didn't know about the :lvalue attribute -- that's quite handy. Sure beats what I was doing for accessors/mutators before:
      sub classvar { my $self = shift; $self->{classvar} = shift if @_; return $self->{classvar}; }
Re: Access package variables of subclass from base class
by GrandFather (Saint) on Mar 04, 2009 at 08:49 UTC

    In C++ parlance you should use a 'pure virtual' member function. In Perl that just means that you assume the derived class will provide a member. Consider:

    use strict; use warnings; package BaseClass; sub new { return bless {}, shift; } sub useVirtualX { my ($self) = @_; return $self->GetX (); } 1; package main; use base 'BaseClass'; my $obj = main->new (); my $privateX = 42; print $obj->useVirtualX (); sub new { return shift->SUPER::new (); } sub GetX { return $privateX; }

    Prints:

    42

    True laziness is hard work
      sub GetX { my ($self) = @_; my $subc = ref($self) ; if ( $subc ne __PACKAGE__ ) { croak "do not call $subc\::SUPER->GetX"; } else { croak "GetX must be implemented by subclass"; } }
Re: Access package variables of subclass from base class
by targetsmart (Curate) on Mar 04, 2009 at 08:32 UTC
    for your requirement, this may help you
    sub id { my $self = shift; my $class = ref $self; no strict 'refs'; return ${'main::'."$class".'::ID'}; }

    Vivek
    -- In accordance with the prarabdha of each, the One whose function it is to ordain makes each to act. What will not happen will never happen, whatever effort one may put forth. And what will happen will not fail to happen, however much one may seek to prevent it. This is certain. The part of wisdom therefore is to stay quiet.
      Interesting, I hadn't stumbled on this one because I had strict enabled. This also works fine, although I don't like disabling strict refs any more than I like using stringy eval:
      no strict 'refs'; return ${ $class . '::ID' };
Re: Access package variables of subclass from base class
by ig (Vicar) on Mar 04, 2009 at 08:50 UTC