How to get parent class instance in Perl - perl

I've used/created lots of classes in Perl and one common thing I need to do in them is to access certain properties of a parent object. For example, let's say I have Class_A and Class_B and they are as simple as this:
Class_A.pm
package Class_A;
use strict;
use warnings;
sub new {
my $class = shift;
my $this = {
history_data => [],
};
bless $this, $class;
}
Class_B.pm
package Class_B;
use strict;
use warnings;
sub new {
my $class = shift;
my $this = {
history_data => [],
};
bless $this, $class;
}
Class_A can use Class_B and create an instance as a property. Both have the property history_data, but if Class_B is an instance created by an instance of Class_A, I want Class_B to use hitory_data in its parent object.
So, what I've done all this time is to just pass a reference when I create the child instance, like this:
#!/usr/bin/perl
use strict;
use warnings;
use Class_A;
use Class_B;
my $class_a = new Class_A;
$class_a->{instance_of_B} = new Class_B parent => $class_a;
I'm doing that very simple, but that instance could be created by some method inside Class_A, when needing to use Class_B. The thing here is that, although Class_B has it's own properties, I want it to use the properties of Class_A when it is its parent.
That works well, but I've several times wondered if it exists a way to do that without passing the parent reference to the child. something like an already implemented way in Perl to call the parent object.
So, that's my question. Is there a way for Class_B to access the instance of its parent Class_A without receiving the reference directly?
Thanks. :-)
EDIT: Another way to put it is this:
Is there a way for Class_B, without having a reference passed, to say "I'm currently an instance of Class_B, living in a property of Class_A, which currently has x, y and z values on its own properties"?

As Michael Carman correctly notes, if you want the Class B object to know which Class A object it is part of, you have to tell it that explicitly.
What you can at least do, however, is to simplify the user interface by having Class A take care of creating its Class B components, e.g. like this:
package Class_A;
use strict;
use warnings;
sub new {
my $class = shift;
my $this = {
history_data => [],
};
bless $this, $class;
$this->{instance_of_B} = Class_B->new( parent => $this );
return $this;
}
Ps. Note that, if the Class A and Class B objects both hold a reference to each other, then what you've just created is a circular reference. Perl only has simple reference-counting garbage collection, which cannot automatically detect such reference circles; this means that, if you then let both objects go out of scope without explicitly breaking at least one link in the circle, the objects will never be garbage-collected (until your program ends) and your code ends up leaking memory.
One way to work around this issue is to weaken one of the references — normally the one from B to A, assuming that the Class A object is the one actually referenced by external code — like this:
package Class_B;
use strict;
use warnings;
use Scalar::Util qw(weaken);
sub new {
my $class = shift;
my $this = { #_ };
weaken $this->{parent};
bless $this, $class;
}
Note that, as a side effect, if someone grabs a reference directly to the Class B object, lets its parent Class A object go out of scope and then tries to call the Class B object's methods directly, the Class B object may find that its parent object has simply disappeared and the reference to it has become undefined. This is, unfortunately, an unavoidable side effect of dealing with Perl's method of garbage collection.

No. An object only knows about it's parent object if you define and set an attribute for it. (A hash entry, in your case.) Perl doesn't track who created whom in the background.

There seems to be a design issue somewhere here. However, see if Aspect::Library::Wormhole might help.
Depending on the exact circumstances, using a dependency injection framework such as Beam::Wire might help.
Update
I am not advocating the code below as a "good" solution or even "good" practice, but here is how Wormhole might help:
package A;
use strict; use warnings;
sub new {
my $class = shift;
bless {
b => undef,
history_data => [],
} => $class;
}
sub set_b {
my $self = shift;
$self->{b} = B->new;
return $self->{b};
}
package B;
use strict; use warnings;
sub new {
my $class = shift;
my $owner = shift;
bless {
history_data => $owner->{history_data} // [],
} => $class;
}
sub add_event {
my $self = shift;
push #{ $self->{history_data} }, [ #_ ];
return;
}
package main;
use strict; use warnings;
use Aspect;
use YAML;
aspect Wormhole => 'A::add_b', 'B::new';
my $x = A->new;
my $y = $x->set_b;
$y->add_event(Horizon => 180, 0, 'Terminal');
print Dump $x;
print Dump $y;

Related

Accessing class variables in inherited function?

I'm trying to create child classes in Perl that inherit class functions from a single parent. I got it to partially work, using the object method syntax Child->inheritedMethod() to call inherited functions outside the child, and my $class=shift; $class->inheritedMethod(); inside the child class, as described here.
However, for inherited methods, it seems control is passed to parent class, and the method is run in the parent scope with the parent variables. For example, this is in the Parent class:
our $VERSION = 0.11;
our $NICKNAME = "Parent Base";
sub version{ $VERSION }
sub whoami{ $NICKNAME }
sub whereami{
my $class = shift;
print "should be printing whereami right now...\n";
print "## In ",(caller(1))[3]," of ",$class->whoami," ",$class->version," in ",__PACKAGE__,"\n";
}
Each child class declares its own $VERSION and $NICKNAME, which I hoped would be accessed in place of the parent variables. But when I call whereami from the child, it gives
## Child::Method of Parent Base 0.11 in Parent.
Questions:
Is there a way around this? Some other module I should use like Moo(se)? Export all the methods instead of inheritance, which I hear shouldn't be done (polluting the namespace, not a problem here)?
Would this still be an issue using objects and object
attributes/variables? I'm trying to avoid it due to my team's
aversion to object-oriented.
Is this how inheritance usually works,
or just Perl? I thought the method would be called within the scope
of the child class, not passed to the parent.
The problem is that the method accesses the variable from the lexical scope where it was declared, i.e. the parent class. Class variables are therefore not the same thing as class attributes.
You can access the correct variable by fully qualifying its name (not possible under strict refs:
#!/usr/bin/perl
use warnings;
use strict;
{ package Parent;
our $package = 'Parent';
sub get_package {
my $class = shift;
{ no strict 'refs';
return (caller(0))[3], $class, ${"$class\::package"}
}
}
}
{ package Son;
use parent 'Parent';
our $package = 'Son';
}
print join ' ', 'Son'->get_package, "\n";
print join ' ', 'Parent'->get_package, "\n";
In Moo*, you can use Moo*X::ClassAttribute:
#!/usr/bin/perl
use warnings;
use strict;
{ package Parent;
use Moo;
use MooX::ClassAttribute;
class_has package => (is => 'ro',
default => 'Parent');
sub get_package {
my $class = shift;
return $class->package;
}
}
{ package Son;
use Moo;
use MooX::ClassAttribute;
extends 'Parent';
class_has package => (is => 'ro',
default => 'Son');
}
print 'Parent'->get_package, "\n";
print 'Son'->get_package, "\n";
Note that MooX::ClassAttribute says
Overriding class attributes and their accessors in subclasses is not yet supported.
Unlike in Moose, you can't use the class_has '+package' => (default => 'Son'); syntax for overriding.

How to access object features in Perl from within the same package

I'm making a Perl module and I am still getting to grips with how Perl deals with objects.
This is the new sub that I wrote to create an object and I have no problem updating elements:
sub new {
my $class = shift;
my ($self) = {
name => undef
};
bless($self, $class);
return $self;
}
sub get_name {
my $self = shift;
$self->{name} = 'Eve';
return $self->{name};
}
I can use the object fine when I call the module and access it from another file, but I want to use the data in the object at other areas in the module code.
So I have no problem doing this:
my $new_object = new ProgramTest; # ProgramTest being the module/package
my $name = get_name();
But I want to use the $self elements in a 'module-internal' method which is never accessed by an outside script. So I want to have something like this:
sub get_variables {
return (name); # I don't know how to get the name here
# (I plan to have other variables, too)
}
I am probably missing something obvious (I'm sure I'll kick myself when I see the solution), so any help appreciated!
I want this so that the rest of the module can use the variables (without changing) as there are conditions that rely on their values.
There's no such thing as internal/private methods in perl objects. Common practise is to start any methods which should not be used publicly with an underscore, but this is not enforced in any way. Also have a look at moose - it takes a lot of the hassle out of OO perl.
With regards to your question the below shows how one module method can call another module method, with both having access to the object data. Again I woulds really recommend you use Moose!
sub publicSub{
my ( $self ) = #_;
return $self->_privateSub();
}
sub _privateSub{
my ( $self ) = #_;
return $self->{name};
}
I think you want class-variables. They are global to a class and all instances of the class (i.e. all the objects you created) can see them. Global in this case means that they are at the ouside-most lexical scope, so all subs can see them.
package ProgramTest;
my $everyone_can_see_this = 1; # lexical scope, but 'global' to the package
sub new {
my $class = shift;
my ($self) = {
name => undef
};
bless($self, $class);
return $self;
}
sub get_var {
my $self = shift;
return ++$everyone_can_see_this;
}
package Main;
my $o1 = ProgramTest->new;
my $o2 = ProgramTest->new;
say $o1->get_var;
say $o2->get_var;
say $o1->get_var;
__END__
2
3
4
But I don't see why you would want to do that. It doesn't make sense (unless you want an object-counter). Don't use it for config values, or you cannot really have objects for different purposes of the same class.
Maybe you want something else. If so, please try to rephrase your question.

How to reclassify Perl object

I'm working with a few Perl packages, we'll call them Some::Parser and Some::Data. A Some::Parser object has methods to return objects of type Some::Data. I have written a class that extends the Some::Data class, let's call it My::Data. Objects of class My::Data are really just objects of class Some::Data, but with additional methods that make it easier to work with.
My problem is that I want to continue to use the Some::Parser class to do the hard work of parsing the data. As I said earlier, Some::Parser objects give me Some::Data objects. Once I have a Some::Data object in hand, is there any way to reclassify it as a My::Data object? How would I do this?
I'm totally willing to change my approach, assuming someone can suggest a better way of doing what I want to do, but writing my own parser is not something I'm interested in doing!
This smells like a bit of a kludge. It might be time to rethink your strategy. For example, maybe you should write My::Parser which returns My::Data objects.
But if you don't want to do that, you can manually use bless to change an object's class:
my $obj = Some::Data->new;
bless $obj, 'My::Data';
See bless in perldoc.
Probably the best way to handle something like this is for Some::Parser to provide a way to specify the class it should be using for data objects. For example, HTML::TreeBuilder provides the element_class method. If you want TreeBuilder to produce something other than HTML::Element nodes, you subclass HTML::TreeBuilder and override element_class to return your desired node class. (The actual code in TreeBuilder is a bit more complex, because there was a different mechanism for doing this prior to HTML-Tree 4, and the new maintainer didn't want to break that.)
I take it that you didn't write Some::Parser, but perhaps it has this capability already. If not, maybe its maintainer will accept a patch. It should be a fairly simple change. You'd just add a data_class method (sub data_class { 'Some::Data' }), and then change Some::Data->new to $self->data_class->new. Then you can subclass Some::Parser to create My::Parser, and just override data_class.
You can rebless anything.
Inheritance in Perl 5 is nothing more than searching #ISA.
You can re-bless the returned object to whatever your heart desires:
#!/usr/bin/perl
package Some::Data;
use strict; use warnings;
sub new { my $class = shift; bless { #_ } => $class }
sub a { $_[0]->{a} }
package My::Data;
use strict; use warnings;
use base 'Some::Data';
sub a_squared {
my $self = shift;
my $v = $self->a;
return $v * $v;
}
package Some::Parser;
use strict; use warnings;
sub new { my $class = shift; bless { #_ } => $class }
sub parse { return Some::Data->new(a => 3) }
package main;
use strict; use warnings;
my $data = Some::Parser->new->parse;
bless $data => 'My::Data';
printf "%.1f\t%.1f\n", $data->a, $data->a_squared;
Alternatively, you can use #cjm's idea:
#!/usr/bin/perl
package Some::Data;
use strict; use warnings;
sub new { my $class = shift; bless { #_ } => $class }
sub a { $_[0]->{a} }
package My::Data;
use strict; use warnings;
use base 'Some::Data';
sub a_squared {
my $self = shift;
my $v = $self->a;
return $v * $v;
}
package Some::Parser;
use strict; use warnings;
sub new { my $class = shift; bless { #_ } => $class }
sub parse {
my $self = shift;
return $self->data_class->new(a => 3);
}
sub data_class { $_[0]->{data_class} }
package main;
use strict; use warnings;
my $data = Some::Parser->new(data_class => 'My::Data')->parse;
printf "%.1f\t%.1f\n", $data->a, $data->a_squared;
I'd consider re-blessing venturesome. Once you have an object you can't really tell if it was created using its constructor ( usually Foo::new() ), or someone re-blessed some other object.
The problem is, some constructors are fat, this means they do a whole lotta more than just blessing something:
sub new {
my $pkg = shift;
my ($required) = #_;
croak "Bad call" unless defined $required;
_do_something_magic ($required);
my $self = { 'foo' => $required };
return bless $self, $pkg;
}
In this case your re-blessed object might not be the one you'll expect later in code.
One may consider constructors with "re-blessing" functionality build in. But such "object converters" will make the design even more complicated.
Stick to the basic definition: "An object is an instance of the class. Forever.".

How should I access instance data in a Perl subclass?

I am extending a module and I want some tips on good practices. Specially namespace conflicts: what exactly are they and how to avoid them.
When extending, should I not access variables in the SUPER class and only alter its state through accessors or object methods? What to do in case there are no (or limited) accessors? Am I "allowed" to access these object variables directly?
Cheers!
It is best to only access things through accessors because this prevents changes in the implementation of the superclass from affecting the subclasses. You should stay far away from anything that starts with an underbar. Those things are private to the class. Try to stay away from anything that is not documented. Relying on those things will get you into trouble. Also, consider using has-a versus is-a relationship.
Let's imagine a widget class. This class has name and price members (note, none of this is particularly good code, I have just tossed of a version with out thinking about it for the sake of an example):
package Widget;
use strict;
use warnings;
sub new {
my $class = shift;
my %args = #_;
return bless {
price => $args{price} || 0,
name => $args{name} || "unkown",
}, $class;
}
sub price { shift->{price} }
sub name { shift->{name} }
1;
You decide to subclass widget to add a weight member:
package Widget::WithWeight;
use strict;
use warnings;
use base 'Widget';
sub new {
my $class = shift;
my %args = #_;
my $self = $class->SUPER::new(%args);
$self->{weight} = $args{weight} || 0;
return bless $self, $class;
}
sub weight { shift->{weight} }
sub price_per_pound {
my $self = shift;
return $self->{price}/$self->{weight};
}
1;
Now imagine the author of the first module changes his/her mind about how to store the price. Perhaps it was stored as a floating point number and the author realized that storing it as an integer number of pennies would be better:
package Widget;
use strict;
use warnings;
sub new {
my $class = shift;
my %args = #_;
if ($args{price}) {
$args{price} =~ s/[.]//;
}
return bless {
price => $args{price} || "000",
name => $args{name} || "unkown",
}, $class;
}
sub price {
my $self = shift;
my $price = $self->{price};
substr($price, -2, 0) = ".";
return $price;
}
sub name { shift->{name} }
1;
Suddenly, your tests will start failing, but if you had used the price accessor instead, you would have been insulated from that change.
Namespace conflicts can happen if you inherit from two modules into one and they both provide (export) the same sub.
I suggest you have a look at Moose, an extension to Perl that provides you with classes and roles. You can avoid many conflicts if you use roles. See http://www.iinteractive.com/moose/
Moose also makes automatic accessors for the class variables, making it safer to access them from inheriting classes.

Perl: oo with use parent - checking if class has a parent

I have some Perl objects which were built some time ago not with Moose,
but with bless, inheritance is implemented using the 'parent' pragma.
Now I would like to know whether there is a way to check whether a certain class has used 'parent' or not.
e.g. if I have two classes
package Animal;
sub new {
my $class = shift;
return bless {}, $class;
}
1;
and
package Cat;
use parent 'Animal';
sub new {
my $class = shift;
return bless {}, $class;
}
1;
would there be some check I could make to determine that the 'Cat' class
has a parent ( do not care which, but not itself ), and Animal does not, given a $foo which is either of them?
I can't picture why you'd ever want to know this, but it's possible using the following:
use mro;
my $inherits = #{ mro::get_linear_isa($class) } > 1;
or
my $isa = do { no strict 'refs'; \#{ $class . '::ISA' } };
my $inherits = #$isa;
Notes:
All classes inherit from UNIVERSAL, but that's ignored unless a class explicitly declares it inherits from it.
These methods don't care how the inheritance was declared (use parent or some other means).