inheritance and order of packages

I

ioneabu

#!/usr/bin/perl

use strict;
use warnings;

package main;

my $q = new MYCGI;
print $q->h1("Hello, World!"),"\n";


package MYCGI;
use CGI;
our @ISA = ('CGI');

produces output:

Can't locate object method "new" via package "MYCGI" at ./test6.pl line
8.

If I put the MYCGI package before main it works (produces expected
output of CGI.pm h1 sub) and if I do this:

#!/usr/bin/perl

use strict;
use warnings;
use MYCGI; #this solves the problem. note: there is no MYCGI.pm
module, just the code below...

my $q = new MYCGI;
print $q->h1("Hello, World!"),"\n";


package MYCGI;
use CGI;
our @ISA = ('CGI');

It produces expected output.

I did not see anything like this in my reading specifically stating
that there has to be a forward declaration of the package or the
package has to come first for the @ISA inheritance mechanism to work
properly.

If I do not bother with inheritance and just implement the needed
function myself, it works fine:

#!/usr/bin/perl

use strict;
use warnings;
#no need to use MYCGI here now that I am not inheriting from CGI in the
package
#why the difference?

my $q = new MYCGI;
print $q->h1("Hello, World!"),"\n";


package MYCGI;
sub new
{
my $class = shift;
my $self = {};
bless($self, $class);
return $self;
}
sub h1
{
my ($self, $text) = @_;
$text = '<h1>'.$text.'</h1>';
return $text;
}


I was wondering why this is with @ISA use, that I have to 'declare' the
package with the @ISA at the top of the main package with a 'use'
statement or put the package ahead of main when, if there is no
inheritence involved in the package, I don't have to do this. I never
would have noticed if I had not tried putting the MYCGI package in the
same file since making it a separate module would have forced my to use
the 'use' statement and it would have just worked as expected.

Thanks!

wana

(OT: maybe rendering problem with groups.google.com and me due to the
fact that I am using OS X at home and sometimes cut and paste from
terminal and sometimes from TextEdit. I did indent properly, but
Google takes my indentation out. I am open to suggestions for other
usenet services that are low cost or free.)
 
M

Matt Garrish

#!/usr/bin/perl

use strict;
use warnings;

package main;

my $q = new MYCGI;
print $q->h1("Hello, World!"),"\n";


package MYCGI;
use CGI;
our @ISA = ('CGI');

produces output:

Can't locate object method "new" via package "MYCGI" at ./test6.pl line
8.

If I put the MYCGI package before main it works (produces expected
output of CGI.pm h1 sub) and if I do this:

That's because the compiler needs to know that MYCGI "isa" CGI before you
try to create the new object. You've obviously figured out some of the less
desirable ways around the problem, but to avoid it altogether just use a
begin block in the MYCGI package:

package MYCGI;

BEGIN {
use CGI;
our @ISA = qw/CGI/;
}

Matt
 
A

Anno Siegel

Matt Garrish said:
That's because the compiler needs to know that MYCGI "isa" CGI before you
try to create the new object. You've obviously figured out some of the less
desirable ways around the problem, but to avoid it altogether just use a
begin block in the MYCGI package:

package MYCGI;

BEGIN {
use CGI;
our @ISA = qw/CGI/;
}

....which can be replaced by

package MYCGI;
use base 'CGI';

Anno
 
I

ioneabu

Thanks! Worked great. I found a nice explanation in the chapter on
compiling in 'Programming Perl'.

wana
 
I

ioneabu

I think I am getting the hang of the concept. This is another example
that was in question, but I think it's the same idea. Does this look
right? Before putting in the BEGIN block, @init was recognized as a
lexical but the data was not there when the constructor made use of it.
The BEGIN block solved the problem just like in the previous example.

#!/usr/bin/perl
use warnings;
use strict;

my $a = one->new;
print $a->to_string(),"\n";

package one;
my @init;
BEGIN
{
@init = (one=>1,two=>2);
}
sub new
{
my $class = shift;
my $self = { @init };
bless($self, $class);
return $self;
}
sub to_string
{
my $self = shift;
join '', map {"$_ = ".$self->{$_}."\n"} keys %{$self};
}


output:

one = 1
two = 2

Thanks!

wana
 
M

Matt Garrish

I think I am getting the hang of the concept. This is another example
that was in question, but I think it's the same idea. Does this look
right? Before putting in the BEGIN block, @init was recognized as a
lexical but the data was not there when the constructor made use of it.
The BEGIN block solved the problem just like in the previous example.

#!/usr/bin/perl
use warnings;
use strict;

my $a = one->new;
print $a->to_string(),"\n";

package one;
my @init;
BEGIN
{
@init = (one=>1,two=>2);
}
sub new
{
my $class = shift;
my $self = { @init };
bless($self, $class);
return $self;
}
sub to_string
{
my $self = shift;
join '', map {"$_ = ".$self->{$_}."\n"} keys %{$self};
}

I suppose that's another way around the problem (although a very convoluted
way to make a hash ref, and not a particularly good use of a begin block).
You could just define $self when you create the object:

sub new
{
my $class = shift;
my $self = { one=>1, two=>2 };
bless($self, $class);
return $self;
}

or even:

sub new
{
my @init = (one=>1,two=>2);
my $class = shift;
my $self = { @init };
bless($self, $class);
return $self;
}

Although I personally don't like the latter. If you want to avoid these
problems altogether, I would suggest bundling the package code off into a
separate .pm file and "use" that (not with an all-lowercase name like you
have, however, as those are reserved for pragmatic modules). The code will
get compiled before your program is run, so you won't need to modify your
code depending on which package is defined first in the file.

Matt
 
I

ioneabu

This was a simplified version of what I was playing around with. I am
trying out a class that generates a table of html widgets to be used
with CGI.pm and I thought that putting a large amount of initialization
data inside of new didn't look right. I am probably going to end up
using a separate sub to initialize either from user supplied arguments
or from a file. I'll definitely put my classes in separate files in
the end, but this is a good way to test them at first.

wana
 
M

Matt Garrish

This was a simplified version of what I was playing around with. I am
trying out a class that generates a table of html widgets to be used
with CGI.pm and I thought that putting a large amount of initialization
data inside of new didn't look right.

Looks shouldn't always be your judge. Your code will be easier to read (and
therefore maintain) if you don't jump through hoops to initialize your
variables. And there is a more elegant solution to intializing a module,
namely write an initialization function and call it when you create the
object. For example:

use warnings;
use strict;

my $a = one->new;
print $a->to_string(),"\n";

package one;

sub new {
my $class = shift;
my $self = {};
bless $self, $class;
$self->init();
return $self;
}

{
sub init {
my $self = $_[0];
my %init = (one=>1,two=>2);
%$self = %init;
}
}


sub to_string
{
my $self = shift;
join '', map {"$_ = ".$self->{$_}."\n"} keys %{$self};
}


Matt
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top