Why does perl object instance overwrite each other - perl

I've written some Perl code which compose two classes inherent from a base one. I suppose it would print something like this
Mik: Meow! Meow!
Sat: Woof! Woof!
But it actually print this way:
Sat: Woof! Woof!
Sat: Woof! Woof!
,
package Animal;
sub new {
my $obj = shift;
my $name = shift;
our %pkg = ( 'name' => $name );
bless \%pkg, $obj;
return \%pkg;
}
package Cat;
#ISA = ("Animal");
sub new {
my $obj = shift;
my $name = shift;
my $self = $obj->SUPER::new($name);
return $self;
}
sub get_name {
my $obj = shift;
return $obj->{'name'};
}
sub talk {
my $obj = shift;
return "Meow! Meow!";
}
package Dog;
#ISA = ("Animal");
sub new {
my $obj = shift;
my $name = shift;
my $self = $obj->SUPER::new( $name );
return $self;
}
sub get_name {
my $obj = shift;
return $obj->{'name'};
}
sub talk {
my $obj = shift;
return "Woof! Woof!";
}
package Main;
my $cat = new Cat('Mike');
my $dog = new Dog('Sat');
print $cat->get_name() . ": " . $cat->talk() , "\n";
print $dog->get_name() . ": " . $dog->talk() , "\n";
But if I change the caller in this way, it prints what I suppose to be. So it is quite strange why the $cat object was overwritten after the $dog was instantiated?
package Main;
my $cat = new Cat('Mily');
print $cat->get_name() . ": " . $cat->talk() , "\n";
my $dog = new Dog('Sat');
print $dog->get_name() . ": " . $dog->talk() , "\n";

Why do you bless into a global variable? Change your constructor to:
sub new {
my $obj = shift;
my $name = shift;
my %pkg = ( 'name' => $name );
bless \%pkg, $obj;
return \%pkg;
}
Better yet, change it to something more idiomatic:
sub new {
my $class = shift;
my $name = shift;
my $self = { name => $name };
return bless $self, $class;
}
Moving on:
Why implement new and get_name in each kind of animal? Both methods can be inherited. While we're at it, we might as well get rid off the messing around with #ISA:
package Animal;
sub new {
my $class = shift;
my $name = shift;
my $self = { name => $name };
return bless $self, $class;
}
sub get_name {
my $self = shift;
return $self->{'name'};
}
package Cat;
use base qw/ Animal /;
sub talk {
my $self = shift;
return "Meow! Meow!";
}
package Dog;
use base qw/ Animal /;
sub talk {
my $self = shift;
return "Woof! Woof!";
}
package Main;
my $cat = Cat->new('Mike');
my $dog = Dog->new('Sat');
print $cat->get_name() . ": " . $cat->talk() , "\n";
print $dog->get_name() . ": " . $dog->talk() , "\n";
May I ask which tutorial or book you are following?
While the above is perfectly fine, you might as well do it the Modern Perl way:
package Animal;
use Moose;
has name => ( required => 1, is => 'rw', isa => 'Str' );
package Cat;
use Moose;
extends 'Animal';
has talk => ( default => "Meow! Meow!", is => 'ro' );
package Dog;
use Moose;
extends 'Animal';
has talk => ( default => "Woof! Woof!", is => 'ro' );
package Main;
my $cat = Cat->new( name => 'Mike');
my $dog = Dog->new( name => 'Sat');
print $cat->name . ": " . $cat->talk , "\n";
print $dog->name . ": " . $dog->talk , "\n";

You have declared the variable to store the instance data using
our %pkg
This is an alias for a single data structure (%Animal::pkg), so all your objects are using the same hash. Change our to my in order to create a new hash each time.
It might be worth noting that "inside-out" objects in Perl can and do use a shared data structure in the package to store instance data, but there is an additional level of abstraction required to make that work, and I wouldn't recommend starting OO Perl with them, they are an acquired taste.

In a nutshell: our declares package variables, so every time our %pkg = (...) is executed, you assign a new value to the same variable. As all \%pkg references point to the same var, all return values of new are the same object. A reference can only be blessed into one class, so the last one wins.
Just change the our to my, and it should work as expected.

Related

How can a perl constructor return a value not just a hashref

I want to create a Perl OO module to return a value like DateTime does, but don't know how to it right now. Anyone's help on this will be appreciated.
Below looks like what I wanted:
use DateTime;
use Data::Printer;
my $time = DateTime->now();
print $time . "\n";
print ref $time;
# p $time;
Output:
2022-11-23T13:22:39
DateTime
What I got:
package Com::Mfg::Address;
use strict;
use warnings;
#constructor
sub new {
my ($class) = #_;
my $self = {
_street => shift || "undefined",
_city => shift || "undefined",
_las_state => shift || "undefined",
_zip => shift || "undefined",
};
bless $self, $class;
return $self;
}
#accessor method for street
sub street {
my ( $self, $street ) = #_;
$self->{_street} = $street if defined($street);
return ( $self->{_street} );
}
#accessor method for city
sub city {
my ( $self, $city ) = #_;
$self->{_city} = $city if defined($city);
return ( $self->{_city} );
}
#accessor method for state
sub state {
my ( $self, $state ) = #_;
$self->{_state} = $state if defined($state);
return ( $self->{_state} );
}
#accessor method for zip
sub zip {
my ( $self, $zip ) = #_;
$self->{_zip} = $zip if defined($zip);
return ( $self->{_zip} );
}
sub print {
my ($self) = #_;
printf( "Address:%s\n%s, %s %s\n\n",
$self->street, $self->city, $self->state, $self->zip );
}
1;
# test.pl
#!/usr/bin/perl -w
use strict;
use Data::Printer;
BEGIN {
use FindBin qw($Bin);
use lib "$Bin/../lib";
}
use Com::Mfg::Address;
my $homeAddr = Com::Mfg::Address->new('#101 Road', 'LA', 'CA', '111111');
print $homeAddr;
# $homeAddr->print();
# p $homeAddr;
But this only gives me:
Com::Mfg::Address=HASH(0xb89ad0)
I am curious if print $homeAddr can give me:
something like #101Road-LA-CA-111111 and it really is object like above print $time . "\n";.
I tried to review DateTime source but still have no clue right now.
You're asking how to provide a custom stringification for the object. Use the following in your module:
use overload '""' => \&to_string;
sub to_string {
my $self = shift;
return
join ", ",
$self->street,
$self->city,
$self->state,
$self->zip;
}
This makes
print $homeAddr;
equivalent to
print $homeAddr->to_string();

Using a perl class object variables in destroy

Can we access object variables inside the destroy method of a perl class.
For eg : i have a perl class as below:
package Person;
sub new {
my $class = shift;
my $self = {
_firstName => shift,
_lastName => shift,
_ssn => shift,
};
# Print all the values just for clarification.
print "First Name is $self->{_firstName}\n";
print "Last Name is $self->{_lastName}\n";
print "SSN is $self->{_ssn}\n";
bless $self, $class;
return $self;
}
I create my object like below:
$object = new Person( "Mohammad", "Saleem", 23234345);
How can I make destroy function so that it will print me like
detroying Mohammad
The DESTROY method gets the same $self reference as its first parameter as all other methods in Perl OOP.
package Person
sub new { ... }
sub DESTROY {
my $self = shift;
print "destroying $self->{_firstName}";
}
package main;
{
my $foo = Person->new( 'foo', 'bar', 123 );
}
This will print
First Name is foo
Last Name is bar
SSN is 123
destroying foo

Perl module that accepts list and creates objects

I am working on an college problem ( in Perl ). We are working on creating modules and I need to write a simple module that "has get/set methods for four attributes: lastname, firstname, full_name and a list of children who are also person objects".
I think I have it down but it's the list of children who are also person objects that throws me. I guess the module needs to accept a list and then create a list of objects? Python is my core language so this one is throwing me. The get/set methods are working fine. Any ideas?
My module is here...
#!/usr/bin/perl
package Person;
sub new
{
my $class = shift;
my $self = {
_firstName => shift,
_lastName => shift,
};
bless $self, $class;
return $self;
}
sub setFirstName {
my ( $self, $firstName ) = #_;
$self->{_firstName} = $firstName if defined($firstName);
return $self->{_firstName};
}
sub getFirstName {
my( $self ) = #_;
return $self->{_firstName};
}
sub setLastName {
my ( $self, $lastName ) = #_;
$self->{_lastName} = $lastName if defined($lastName);
return $self->{_lastName};
}
sub getLastName {
my( $self ) = #_;
return $self->{_lastName};
}
sub getFullName {
my( $self ) = #_;
return $self->{_lastName}.",".$self->{_firstName};
}
1;
My code is here.....
#!/usr/bin/perl
use Person;
$object = new Person("Elvis","Presley");
# Get first name which is set using constructor.
$firstName = $object->getFirstName();
$lastName = $object->getLastName();
$fullname = $object->getFullName();
print "(Getting) First Name is : $firstName\n";
print "(Getting) Last Name is: $lastName\n";
print "(Getting) Full Name is: $fullname\n";
Just use a list of objects in the setter:
#! /usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
{ package Person;
sub new {
my $class = shift;
my $self = {
_firstName => shift,
_lastName => shift,
_children => [],
};
return bless $self, $class
}
sub setFirstName {
my ($self, $firstName) = #_;
$self->{_firstName} = $firstName if defined $firstName;
return $self->{_firstName}
}
sub getFirstName {
my ($self) = #_;
return $self->{_firstName}
}
sub setLastName {
my ($self, $lastName) = #_;
$self->{_lastName} = $lastName if defined $lastName;
return $self->{_lastName}
}
sub getLastName {
my ($self) = #_;
return $self->{_lastName}
}
sub getFullName {
my ($self) = #_;
return $self->{_lastName} . ', ' . $self->{_firstName}
}
sub getChildren {
my ($self) = #_;
return #{ $self->{_children} }
}
sub setChildren {
my ($self, #children) = #_;
$self->{_children} = [ #children ];
}
}
my $object = 'Person'->new('Elvis', 'Presley');
# Get first name which is set using constructor.
my $firstName = $object->getFirstName;
my $lastName = $object->getLastName;
my $fullname = $object->getFullName;
$object->setChildren('Person'->new('Lisa', 'Presley'),
'Person'->new('Deborah', 'Presley'));
say "(Getting) First Name is: $firstName";
say "(Getting) Last Name is: $lastName";
say "(Getting) Full Name is: $fullname";
say "Children: ";
say $_->getFullName for $object->getChildren;
Note that there are modules to make building objects easier, e.g. Moo:
#! /usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
{ package Person;
use Moo;
has first_name => (is => 'ro');
has last_name => (is => 'ro');
has full_name => (is => 'lazy');
has _children => (is => 'ro',
init_arg => undef,
default => sub { [] });
sub _build_full_name {
my ($self) = #_;
return $self->last_name . ', ' . $self->first_name
}
sub add_child {
my ($self, $child) = #_;
push #{ $self->_children }, $child
}
sub children {
my ($self) = #_;
return #{ $self->_children }
}
}
my $object = 'Person'->new(first_name => 'Elvis',
last_name => 'Presley');
# Get first name which is set using constructor.
my $firstName = $object->first_name;
my $lastName = $object->last_name;
my $fullname = $object->full_name;
$object->add_child($_) for 'Person'->new(first_name => 'Lisa',
last_name => 'Presley'),
'Person'->new(first_name => 'Deborah',
last_name => 'Presley');
say "(Getting) First Name is: $firstName";
say "(Getting) Last Name is: $lastName";
say "(Getting) Full Name is: $fullname";
say "Children: ";
say $_->full_name for $object->children;
The requirement means that there should be an attribute which can accommodate a collection of objects, so a reference to an array. This is defined in the constructor
sub new
{
my $class = shift;
my $self = {
_firstName => shift,
_lastName => shift,
_children => [ #_ ],
};
bless $self, $class;
return $self;
}
where [ ] creates an anonymous array and returns its reference, which is a scalar so it can be used for a hash value. The #_ in it contains the optional rest of the arguments (Person objects) after the class and names have been shift-ed.
Arguments need be checked but this gets hard with a plain list, when they are used positionally. Instead, consider using named parameters, ie. passing a hash(ref) to the constructor, with which it's easy to check which arguments have or have not been supplied.
Next, you need a method to add children to this attribute, for example
sub add_children {
my ($self, #children) = #_; # and check what's passed
push #{$self->{_children}}, #children;
return $self; # for chaining if desired
}
Finally, when you invoke this method you pass objects of the class Person to it
use warnings;
use strict;
use Person;
my $object = Person->new('Elvis', 'Presley');
my $child = Person->new('First', 'Last');
$object->add_children( $child );
or, if there is no use of a $child variable (object) in the rest of the code
$object->add_children( Person->new(...) );
You can add a list of children, add_children($c1, $c2, ...), for example to initially populate the data structure, or can add them individually as they appear.
A list of Person children can be used in the constructor as well
my $obj = Person->new('First', 'Last', $c1, $c2,...);
This gets clearer and far more flexible with mentioned named parameters, which are unpacked and sorted out in the constructor. But more to the point, once you learn the Perl's native OO system look at modules for this, best Moose and its light-weight counterpart Moo.
Comments
Always have use warnings; and use strict; at the beginning
Don't use the indirect object notation
my $obj = new ClassName(...); # DO NOT USE
See this post and this great example. The fact that it can be used to call a constructor is really an abuse of its other legitimate uses. Use a normal method call
my $obj = ClassName->new(...);
It's great that your college is teaching you Perl, but slightly disappointing that they're teaching you the "classic" version of Perl OO, when in the real world most OO work in Perl uses a framework like Moo or Moose.
For interest, I've included a Moo version of the Person object below:
package Person;
use Moo;
use Types::Standard qw[Str ArrayRef Object];
has first_name => (
is => 'rw',
isa => Str,
required => 1,
);
has last_name => (
is => 'rw',
isa => Str,
required => 1,
);
has children => (
is => 'rw',
isa => ArrayRef[Object],
);
sub full_name {
my $self = shift;
return $self->first_name . ' ' . $self->last_name;
}
1;
And here's a simple test program:
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use Person;
my $elvis = Person->new(
first_name => "Elvis",
last_name => "Presley",
children => [Person->new(
first_name => 'Lisa Marie',
last_name => 'Presley',
)],
);
my $first_name = $elvis->first_name;
my $last_name = $elvis->last_name;
my $full_name = $elvis->full_name;
say "(Getting) First Name is : $first_name";
say "(Getting) Last Name is: $last_name";
say "(Getting) Full Name is: $full_name";
say "Elvis's first child is ", $elvis->children->[0]->full_name;
A few things to note:
Always include use strict and use warnings in your code
Always use Class->new in preference to new Class
Perl programmers prefer snake_case to camelCase
Moo likes you to use named parameters to an object constructor
Declarative attributes (using has) are far less repetitive than writing all your own getter and setter methods
People programmers tend to prefer a single method (foo() which can be used as both a getter and a setter over separate get_foo() and set_foo() methods.

Is it possible to register a function to preprocess log messages with Log::Log4perl?

In this example:
$logger->debug({
filter => \&Data::Dumper::Dumper,
value => $ref
});
I can pretty print my references instead of ARRAY(0xFFDFKDJ). But it's too boring to type that long code every time. I just want:
$logger->preprocessor({
filter => \&Data::Dumper::Dumper,
value => $ref
});
$logger->debug( $ref, $ref2 );
$logger->info( $array );
And $ref, $ref2, and $array will be dumped by Data::Dumper.
It there a way to do this?
UPD
With help of your answers I do the patch
Now you just:
log4perl.appender.A1.layout=FallbackLayout
log4perl.appender.A1.layout.chain=PatternLayout
log4perl.appender.A1.layout.chain.ConversionPattern=%m%n
log4perl.appender.A1.warp_message = sub { $#_ = 2 if #_ > 3; \
return #_; }
# OR
log4perl.appender.A1.warp_message = main::warp_my_message
sub warp_my_message {
my( #chunks ) = #_;
use Data::Dump qw/ pp /;
for my $msg ( #chunks ) {
$msg = pp $msg if ref $msg;
}
return #chunks;
}
UPD2
Or you can use this small module
log4perl.appender.SomeAPP.warp_message = Preprocess::Messages::msg_filter
log4perl.appender.SomeAPP.layout = Preprocess::Messages
package Preprocess::Messages;
sub msg_filter {
my #chunks = #_;
for my $msg ( #chunks ) {
$msg = pp $msg if ref $msg;
}
return #chunks;
};
sub render {
my $self = shift;
my $layout = Log::Log4perl::Layout::PatternLayout->new(
'%d %P %p> %c %F:%L %M%n %m{indent=2}%n%n'
);
$_[-1] += 1; # increase level of the caller
return $layout->render( join $Log::Log4perl::JOIN_MSG_ARRAY_CHAR, #{ shift() }, #_ );
}
sub new {
my $class = shift;
$class = ref ($class) || $class;
return bless {}, $class;
}
1;
Yes, of course you can set 'warp_message = 0' and combine msg_filter and render together.
log4perl.appender.SomeAPP.warp_message = 0
log4perl.appender.SomeAPP.layout = Preprocess::Messages
sub render {
my($self, $message, $category, $priority, $caller_level) = #_;
my $layout = Log::Log4perl::Layout::PatternLayout->new(
'%d %P %p> %c %F:%L %M%n %m{indent=2}%n%n'
);
for my $item ( #{ $message } ) {
$item = pp $item if ref $item;
}
$message = join $Log::Log4perl::JOIN_MSG_ARRAY_CHAR, #$message;
return $layout->render( $message, $category, $priority, $caller_level+1 );
}
The easy way: use warp_message
The easiest way to do this is to create a custom appender and set the warp_message parameter so you can get the original references that were passed to the logger:
package DumpAppender;
use strict;
use warnings;
use Data::Dumper;
$Data::Dumper::Indent = 0;
$Data::Dumper::Terse = 1;
sub new {
bless {}, $_[0];
}
sub log {
my($self, %params) = #_;
print ref($_) ? Dumper($_) : $_ for #{ $params{message} };
print "\n";
}
package main;
use strict;
use warnings;
use Log::Log4perl;
Log::Log4perl->init(\q{
log4perl.rootLogger=DEBUG,Dump
log4perl.appender.Dump=DumpAppender
log4perl.appender.Dump.layout=NoopLayout
log4perl.appender.Dump.warp_message=0
});
my $logger = Log::Log4perl->get_logger;
$logger->debug(
'This is a string, but this is a reference: ',
{ foo => 'bar' },
);
Output:
This is a string, but this is a reference: {'foo' => 'bar'}
Unfortunately, if you take this approach, you're stuck writing your own code to handle layouts, open files, etc. I wouldn't take this approach except for very simple projects that only need to print to screen.
A better way: composite appender
A better approach is to write your own composite appender. A composite appender forwards messages on to another appender after manipulating them somehow, e.g. filtering or caching them. With this approach, you can write only the code for dumping the references and let an existing appender do the heavy lifting.
The following shows how to write a composite appender. Some of this is explained in the docs for Log::Log4perl::Appender, but I copied much of it from Mike Schilli's Log::Log4perl::Appender::Limit:
package DumpAppender;
use strict;
use warnings;
our #ISA = qw(Log::Log4perl::Appender);
use Data::Dumper;
$Data::Dumper::Indent = 0;
$Data::Dumper::Terse = 1;
sub new {
my ($class, %options) = #_;
my $self = {
appender => undef,
%options
};
# Pass back the appender to be limited as a dependency to the configuration
# file parser.
push #{ $options{l4p_depends_on} }, $self->{appender};
# Run our post_init method in the configurator after all appenders have been
# defined to make sure the appenders we're connecting to really exist.
push #{ $options{l4p_post_config_subs} }, sub { $self->post_init() };
bless $self, $class;
}
sub log {
my ($self, %params) = #_;
# Adjust call stack so messages are reported with the correct caller and
# file
local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 2;
# Dump all references with Data::Dumper
$_ = ref($_) ? Dumper($_) : $_ for #{ $params{message} };
$self->{app}->SUPER::log(
\%params,
$params{log4p_category},
$params{log4p_level}
);
}
sub post_init {
my ($self) = #_;
if(! exists $self->{appender}) {
die "No appender defined for " . __PACKAGE__;
}
my $appenders = Log::Log4perl->appenders();
my $appender = Log::Log4perl->appenders()->{$self->{appender}};
if(! defined $appender) {
die "Appender $self->{appender} not defined (yet) when " .
__PACKAGE__ . " needed it";
}
$self->{app} = $appender;
}
package main;
use strict;
use warnings;
use Log::Log4perl;
Log::Log4perl->init(\q{
log4perl.rootLogger=DEBUG, Dump
log4perl.appender.Dump=DumpAppender
log4perl.appender.Dump.appender=SCREEN
log4perl.appender.SCREEN=Log::Log4perl::Appender::Screen
log4perl.appender.SCREEN.layout=PatternLayout
log4perl.appender.SCREEN.layout.ConversionPattern=%d %p %m%n
});
my $logger = Log::Log4perl->get_logger;
$logger->debug(
'This is a string, but this is a reference: ',
{ foo => 'bar' },
);
Output:
2015/09/14 13:38:47 DEBUG This is a string, but this is a reference: {'foo' => 'bar'}
Note that you have to take some extra steps if you initialize Log::Log4perl via the API instead of via a file. This is documented in the composite appenders section of the Log::Log4perl::Appender documentation.

Define the method in the constructor of class in perl

I am reading code snippets like below:
sub new {
my $pkg = shift;
my $args = shift;
my #keys = keys %$args;
my $self = bless \%{$args}, $pkg;
$self->{'__properties'} = \#keys;
my $class = ref($self);
foreach my $meth (#keys) {
if (! $self->can($meth)) {
no strict "refs";
*{ $class . "::" . $meth } = sub {
my $instance = shift;
return $instance->{$meth};
};
}
}
return $self;
}
In the foreach loop, it seems that it creates some methods according to the parameters. There are two lines which I don't understand.Could someone help me? What's the * and {} used for?
no strict "refs";
*{ $class . "::" . $meth }
Best Regards,
This creates a symbol table alias.
The right side contains a reference to a function, so Perl will alias it to the subroutine $meth in the package $class.
See Symbol Tables in perlmod.
As eugene y have already explained, those lines manipulate the symbol table. In practical terms, they do so in order to create read-only accessor methods in the class based on whatever arbitrary list of attributes get passed into the constructor:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.10.0;
package SomeClass;
sub new {
my $pkg = shift;
my $args = shift;
my #keys = keys %$args;
my $self = bless \%{$args}, $pkg;
$self->{'__properties'} = \#keys;
my $class = ref($self);
foreach my $meth (#keys) {
if (!$self->can($meth)) {
no strict "refs";
*{$class . "::" . $meth} = sub {
my $instance = shift;
return $instance->{$meth};
};
}
}
return $self;
}
package main;
my $foo = SomeClass->new({foo => 5}); # Creates SomeClass::foo
say $foo->foo; # 5
my $bar = SomeClass->new({foo => 3, bar => 7}); # Creates SomeClass::bar
say $bar->foo; # 3
say $bar->bar; # 7
say $foo->bar; # undef - ::bar was added to all instances of SomeClass
say $foo->baz; # Boom! No such method.
Personally, I think this is questionable OO practice (a class should generally have a known set of attributes instead of potentially adding new ones each time an instance is constructed), but that's what it does...