Coder's Guild Mailing List

Re: Does Perl have virtual functions?

Posted by Tom Christiansen on 1999-11-20

On Fri, 19 Nov 1999 22:46:19 -0500
    Frank Hale <frankhale@xxxxx.xxx> wrote
    using Mozilla 4.61 [en] (X11; I; Linux 2.2.13 i686)
  in <3836198B.A1992B65@xxxxx.xxx>:

>I am writing a perl module and I need to know how I can implement a
>virtual function. 

Could you explain that without using the C++ term?  It might help for
others to understand what you really want.  I'll make some guesses,
but they're just guesses.  I have a feeling that you might have so
much C++ brain-washing that you might be asking the wrong question,
and that any answer provided would be consequently of limited if not in
fact dubious value.

>Basically when you use this module you will have to
>define one of its functions. 

This is the hard part, the place where I have to guess most.  What is the
value of "you", of "have to", and, I suppose, of "function" as used above?

Do you mean that you wish to define a class V such that it provides no
method calls that are actually productive, that is, which do something
useful?  Rather, you expected all derived classes to fill in those
functions for you?  What then is the purpose of V?  Or do you have
some productive methods and others which are not?  Why would you have
V contain stub definitions at all, if they're not going to do anything?
Is it because that way you could catch a derived class that didn't bother
to, or remember to, override V's nonproductive methods?

    @D::ISA = ('V');
    sub V::snaggle { die "snaggle method not overridden" }
    sub V::thwap   { die "thwap method not overridden"   }
    sub D::thwap   { print "you've been thwapped\n"      }

Now a call to D::thwap does something ostensibly productive, but to
D:snaggle just blows up.  But of course, if you didn't defined V::snaggle
at all, D::snaggle would still blow up.  So I don't understand what this
is gaining you.

Or are you expecting some magical compile-time verification of
such a thing?  Perl certainly doesn't automatically enforce this
kind of austere and unforgiving discipline, but it does provide
sufficient tools that the creative yet masochistic programmer could
probably implement something approximating this functionality if 
he were so mercilessly inclined.  

Here's one approach (well, two, actually):

    package V;

    use Carp;

    @Required_Methods = qw/thwap snaggle blop/;

    for my $meth (@Required_Methods) {
	no strict 'refs';
	*$meth = sub { confess "I should never be called };
    } 

    sub V::persnickitate {
	my $class = shift;
	return if $class eq __PACKAGE__;
	confess "not a class method call (obj $class)" if ref $class;
	confess "not a child of mine" unless $class->isa(__PACKAGE__);
	my @missing;
	for my $meth ( @Required_Methods ) {
	    no strict 'refs';
	    push @missing, $meth
		unless defined &{ $class . "::" . $meth };
	} 
	return unless @missing;
	confess "class $class missing method implementation: @missing\n";
    } 

Now when you write a class D, you write:

    package D;
    use V;
    @ISA = ('V');
    D->persnickitate();
    sub thwap   { return "happy" }
    sub snaggle { return "happy" }
    sub blop    { return "happy" }

But if you forgot to persnickify, the stubs would still blow
up if you called them on a D object.

You don't have to stop there.  You could set up some code in V 
that detects who's requiring it, and persnickifies them whether
they like it or not.  Just put this at the bottom of V, not in 
a function:

    caller->persnickify();

But now you've got a problem because you'll get persnickified at
compile time, during use V, before the compiler has enterred the
requisite symbols into D's symbol table, which will cause your
persnickety verifier to fail spectacularly.   It won't yet have
its @ISA set-up, either.

So you'll have to make sure that your derived 

    package D;
    @ISA = ('V');	# must precede the following:
    require V;		# must be require, not use
    sub thwap   { return "happy" }
    sub snaggle { return "happy" }
    sub blop    { return "happy" }

So yes, Perl makes this possible.  Not pleasant.  Not pretty.
But possible.  But I'll tell you what, this seems like teaching a pig
to sing, or a canary to root out truffles.  It's all so Just what kind
of system are you building?  I keep wondering whether and why you really
have to do this.

>Each object will define this function
>differently so I need to use a virtual function. 

I think you are confused.  Perl doesn't care.

    package V;
    sub new { return bless {} => shift } 

    package D;
    @ISA = ('V');	
    sub thwap   { return "happy" }
    sub snaggle { return "happy" }
    sub blop    { return "happy" }

    package F;
    @ISA = ('V');	
    sub thwap   { return "sad" }
    sub snaggle { return "sad" }
    sub blop    { return "sad" }

What ever gives you the idea that you need a V::thwap just because you'll be
someday wanting to be D::thwap or F::thwap methods?  

Could it be...

    coult it be...

	could it be C++ *brain*damage*?  

Escape the shackles of bondage!  The cage is illusory!  Fly!  Be free!

>Is there a way to do
>virtual functions in perl?

First of all, you mean method, not function, and in any event, you
wouldn't be far off the mark if you consider all methods to be virtual
in Perl.  But you'd be even better to disabuse yourself of the terminology
completely, because, owing to a blessèd lack of an isomorphic mapping
of concepts betwixt the two programming languages, it really doesn't
really make as much sense as you appear to think it does.

I have a new manpage, perltootc, that might help you liberate yourself
>from the bondage of despair and to start thinking about objects more in
Perl.  This manpage is included in the latest Perl releases, and is also
available in http://doriath.perl.com/misc/perltootc.html if you'd like.
I must also recommend Dr Damian Conway's textbook on Object-Oriented Perl,
published by Manning.

--tom