views:

1898

answers:

6

How do I apply 'use base' in Perl to inherit subs from some base module?

I'm used to C++ inheritance mechanics, and all the sites I googled for this caused more confusion then help. I want to do something like the following:

#! /usr/bin/perl
#The base class to inherit from
use strict;
use warnings;

package 'TestBase';

#-------------------------------
sub tbSub
{
    my ($self, $parm) = @_;
    print "\nTestBase: $parm\n";
}

1;

.

#! /usr/bin/perl
#The descendent class
use strict;
use warnings;

use base qw(TestBase);
sub main;
sub mySub;

#-------------------------------
#Entry point...
main();

#---code------------------------
sub main
{
    mySub(1);
    tbSub(2);
    mySub(3);
}

#-------------------------------
sub mySub
{
    my $parm = shift;
    print "\nTester: $parm\n";
}

Perl complains/cannot find tbSub.

+4  A: 

Perl's inheritance inherits methods, not functions. That means you will have to call

main->tbSub(2);

However, what you really want is to inherit the method into a proper class:

package Derived;
use base "TestBase";

package main;
Derived->somemethod("foo");

Calling methods in the current package as functions won't pass in the $self or "this" object nor the class name magically. Internally,

Class->somemethod("foo")

essentially ends up being called as

Class::somemethod("Class", "foo")

internally. Of course, this assumes Class has a subroutine/method named "somemethod". If not, the superclasses of Class will be checked and if those don't have a method "somemethod" either, you'll get a fatal error. (Same logic applies for $obj->method("foo").)

tsee
No, it doesn’t end up called like that – it will look up the inheritance hierarchy so may call a sub in a completely different package. Also, your examples are not valid syntax. (I know what you were trying to say, but how you said it is bound to confuse someone as confused as the OP even further.)
Aristotle Pagaltzis
Of course it walks the inheritance tree. The question is about inheritance after all. Note that the OP is used to using C++ inheritance, so I thought that'd be obvious.
tsee
+9  A: 

The C++ mechnics aren't much different than the Perl mechanics: To use inheritance, you need two classes: the base class and the inheriting class. But you don't have any descendent class.

You are also lacking a constructor. Unlike C++, Perl will not provide a default constructor for you.

Your base class contains a bad syntax error, so I guess you didn't try the code before posting.

Finally, as tsee already observed, you will have to let Perl know whether you want a function call or a method call.

What you really want would look something like this:

my $foo = TestDescendent->new();
$foo->main();


package TestBase;

sub new {
   my $class = shift;
   return bless {}, $class;
}

sub tbSub
{
   my ($self, $parm) = @_;
   print "\nTestBase: $parm\n";
}

package TestDescendent;
use base 'TestBase';

sub main {
    my $self = shift;
    $self->mySub( 1 );
    $self->tbSub( 2 );
    $self->mySub( 3 );
}

sub mySub
{
    my $self = shift;
    my $parm = shift;
    print "\nTester: $parm\n";
}

1;
innaM
+3  A: 

Hi slashmais,

It seems to me, you are mixing up two things here: Object-Oriented and Procedural Perl. Perl OO is kind of "different" (as in not mainstream but workable).

Your TestBase.pm module seems to expect to be run as a Perl object (Perl oo-style), but your Perl script wants to access it as "normal" module. Perl doesn't work the way C++ does (as you realised) so you would have to construct your code differently. See Damian Conway's books for explanations (and smarter code than mine below).


Procedural:

#! /usr/bin/perl
#The module to inherit from

package TestBase;
  use strict;
  use warnings;

  use Exporter ();
  our @ISA         = qw (Exporter);
  our @EXPORT      = qw (tbSub);

#-------------------------------
sub tbSub
{
    my ($parm) = @_;
    print "\nTestBase: $parm\n";
}

1;

.

#! /usr/bin/perl
#The descendent class
use strict;
use warnings;

use TestBase; 
sub main;
sub mySub;

#-------------------------------
#Entry point...
main();

#---code------------------------
sub main
{

    mySub(1);
    tbSub(2);
    mySub(3);
}

#-------------------------------
sub mySub
{
    my $parm = shift;
    print "\nTester: $parm\n";
}


Perl OO

#! /usr/bin/perl
#The base class to inherit from

package TestBase;
  use strict;
  use warnings;

#-------------------------------
sub new { my $s={ };
    return bless $s;
}
sub tbSub
{
    my ($self,$parm) = @_;
    print "\nTestBase: $parm\n";
}

1;

.

#! /usr/bin/perl
#The descendent class
use strict;
use warnings;

use TestBase; 
sub main;
sub mySub;

#-------------------------------
#Entry point...
main();

#---code------------------------
sub main
{
    my $tb = TestBase->new();
    mySub(1);
    $tb->tbSub(2);
    mySub(3);
}

#-------------------------------
sub mySub
{
    my $parm = shift;
    print "\nTester: $parm\n";
}
lexu
+2  A: 

As a sidenote, there is little good reason to use base rather than the newer use parent.

Aristotle Pagaltzis
Interesting. But is there any good reason to use parent?
innaM
Yes, better predictability of what will happen. base.pm conflates a bunch of behaviours and tries to guess in some rare circumstances. You are unlikely to run into the problems this can cause, but when you do, it bites. parent.pm does not fail mysteriously.
Aristotle Pagaltzis
Fair enough. Thanks!
innaM
Unless running easily on older Perls without an additional depdency is desired, and base.pm doesn't do anything that bites you in the ass in your normal usage of it :)
David Precious
+3  A: 

You should have a look at using Moose which is a postmodern object system for Perl5. You will probably find it a lot easier to grasp than using standard Perl OO semantics... especially when coming from another OO language.

Here's a Moose version of your question....

package TestBase;
use Moose;

sub tbSub {
   my ($self, $parm) = @_;
   print "\nTestBase: $parm\n";
}


package TestDescendent;
use Moose;
extends 'TestBase';

sub main {
    my $self = shift;
    $self->mySub( 1 );
    $self->tbSub( 2 );
    $self->mySub( 3 );
}

sub mySub {
    my ($self, $parm) = @_;
    print "\nTester: $parm\n";
}


package main;
my $foo = TestDescendent->new();
$foo->main

The differences are....

  • Constructor automatically created for you &
  • Inheritance defined by "extends" command instead of "use base".

So this example only covers the tip of the Moose iceberg ;-)

/I3az/

draegtun
A: 

OO syntax uses the -> operator to separate the message and arguments from the receiver of the message. A short illustration below.

You->do_something( @params );

OR 

$you->do_something( @params );

package A;

sub do_neat_thing { 
    my ( $class_or_instance, @args ) = @_;
    my $class = ref( $class_or_instance );
    if ( $class ) {
         say "Instance of '$class' does a neat thing.";
    }
    else { 
        say "$class_or_instance does a neat thing.";
    }
}

...
package main;
A->do_neat_thing();      # A does a neat thing.
my $a_obj = A->new();
$a->do_neat_thing();     # Instance of 'A' does a neat thing.
Axeman
There is no restriction on `use` ing other modules before `use base`.
Ether