Reputation: 213
I have a Perl program and packages Worker
and Log
.
The Worker does almost all calculations, and I want to pass an object by reference to the Worker subroutine, as well as some other parameters (scalar and an array). I have seen examples like this and this.
They handle this by putting @_
in subs, then manipulating the object. I also found a way to manipulate them by using the index, like @{$_[i]}
. Problem is, when I try the code like so, I get an error:
Can't call method "write" on unblessed reference at ...
Code snippets below.
Main:
use strict;
use warnings;
use Log;
use Worker;
my $log = Log->new();
my $worker = Worker->new();
my $scalar = "SomeURLhere";
my @array = ('red','blue','white');
# I do some stuff with $log object
#...
# Now I want to pass data to the Worker
$worker->subFromWorker($scalar, \$log, \@array);
use strict;
use warnings;
package Worker;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub subFromWorker{
my ($self) = shift;
my $scalar = $_[0];
#my ($log) = $_[1];
my @array = @{$_[2]};
foreach my $item (@array){
print $item;
}
$_[1]->write("The items from url $scalar are printed.");
#Same thing happens if I use $log here
}
In C#, this is handled in a different way - you can send a parameter to a method by value or by reference, and then do what you want in a specialized method (method is pre-written to handle parameters by reference or value). I thought that in Perl sending using \parameter
will send the reference.
Upvotes: 3
Views: 3600
Reputation: 126722
You have read about the issues that prevent your program from working, but there are a few other things you should be aware of
Perl lexical identifiers and subroutine/method names consist of alphanumerics and underscore. Capital letters are reserved for global identifiers, such as package names like Worker
and Log
.
Packages that you use
or require
should end with the statement 1;
so as to return a true value when they are imported, otherwise your program may fail to compile.
If a subroutine that you are writing happens to be a method, then it is clearest to start it by shifting off the $self
parameter and making a copy of the rest:
my $self = shift;
my ($p1, $p2, $p3) = @_;
It is rare to use elements of @_
directly unless you're desperate for the minimal speed bonus
It is usually best to work directly with an array reference rather than copying the array, especially if it may be large.
Here is how I would code your program and associated modules:
use strict;
use warnings;
use Worker;
use Log;
my $log = Log->new;
my $worker = Worker->new;
my $scalar = 'SomeURLhere';
my @array = qw/ red blue white /;
$worker->worker_method($scalar, $log, \@array);
use strict;
use warnings;
package Worker;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub worker_method {
my $self = shift;
my ($scalar, $log, $array) = @_;
foreach my $item (@$array) {
print $item, "\n";
}
$log->write("The items from URL $scalar are printed.");
}
1;
use strict;
use warnings;
package Log;
sub new {
my $class = shift;
bless {}, $class;
}
sub write {
my $self = shift;
my ($text) = @_;
print "Logging: $text\n"
}
1;
red
blue
white
Logging: The items from URL SomeURLhere are printed.
Upvotes: 3
Reputation: 61512
A more common pattern is to use List assignment to unpack @_
into multiple variables all at once:
sub subFromWorker {
my ($self, $scalar, $log_ref, $array) = @_;
...
}
In reference to your specific problem:
my $log = Log->new();
$log
is already a reference to your object, using \$log
creates a reference to that reference which is not probably not what you want. You can handle this two ways:
$log
:
$worker->subFromWorker($scalar, $log, \@array);
$log
in subFromWorker
before calling functions on it:
$$log_ref->write('...');
Upvotes: 0
Reputation: 69244
Objects are references. References are scalar values.
If you want to pass arrays or hashes into a subroutine then you usually want to pass references to them - because Perl parameter passing works far better with scalar values.
But $log
is already a reference to your object. Therefore you don't need to take a reference to it. You end up passing a reference to a reference. So when you copy that parameter into $log
inside your subroutine you have an extra, unnecessary, level of references.
The fix is to just pass the $log
scalar into the subroutine.
$worker->subFromWorker($scalar, $log, \@array); # $log, not \$log
Everything else will then work fine.
Upvotes: 7