Reputation: 323
I have a problem when using a variable to reference a module, it seems to mess up the passing of the variables:
package TOTO;
use Data::Dumper;
sub print {
print Dumper(@_);
}
package main;
TOTO::print('Hello World');
print ">>>>>>>>>>>\n";
my $package = 'TOTO';
$package->print('Hello World');
And the output is:
$VAR1 = 'Hello World';
>>>>>>>>>>>
$VAR1 = 'TOTO';
$VAR2 = 'Hello World';
Any advice on how to avoid having TOTO
passed as the first variable?
Upvotes: 5
Views: 1221
Reputation: 66883
Short: The observed behavior comes from use of ->
on a package name.
The arrow operator is used with a reference or with an object, which itself is a reference to a data structure that has been bless
-ed into its class. (Or with a class name, see below.) That object or the class name is quietly passed as the first argument so that the whole system would work. Note that the package in the question does not define a class (objects cannot be created with it).
"-> " is an infix dereference operator, just as it is in C and C++. If the right side is either a [...] , {...} , or a (...) subscript, then the left side must be either a hard or symbolic reference to an array, a hash, or a subroutine respectively. (Or technically speaking, a location capable of holding a hard reference, if it's an array or hash reference being used for assignment.) See perlreftut and perlref.
It continues, to statements of direct interest in this problem
Otherwise, the right side is a method name or a simple scalar variable containing either the method name or a subroutine reference, and the left side must be either an object (a blessed reference) or a class name (that is, a package name). See perlobj.
So in uses related to classes the left-hand side may contain the class name, and class methods can then be invoked on it (or it can be just queried). Given that a class is a package then this is a package name.
The situation in the question falls within this so the package name is passed to the subroutine. However, according to the above quote it seems that the sub can only be a method, which isn't the case here. So it may be that this use of ->
should really be disallowed. Either way, using it on a package which isn't a class strikes me as mistaken.
Update to clarification. This use was intended, to resolve an ambiguity in which package was loaded. The package name is saved into a variable and then the sub invoked on it, using the arrow operator. In this case code would have to be added to the sub to handle the first argument (package name) which is passed regardless of the invocation, by the courtesy of the arrow operator. But then we would have to allow a case when this is invoked on an object, ending up with a code that covers two distinct uses. I believe that it is better to change to a design that does not involve all this.
If you want to use a package, say as a library
File TOTO.pm
pacakge TOTO;
use Exporter;
our (@ISA, @EXPORT_OK);
@ISA = ('Exporter');
@EXPORT_OK = qw(prn); # This can be asked for by user of package
use Data::Dumper;
sub prn {
print Dumper(@_);
}
1; # important for 'require' when this is used
I've changed the sub name to prn
so that it's not a Perl library function. The main script
use warnings;
use strict;
use TOTO qw(prn);
prn("Hello World");
The fully qualified name TOTO::prn()
can always be used. If you wanted to make this a class that would require a bit more in the package.
This package, TOTO
, does not export anything by default, unless asked for. That's what @EXPORT_OK
sets up and that's why we need to list functions to import into main::
when use TOTO
. Start, for example, with perlmod
Upvotes: 2
Reputation: 126722
Here's the issue
Any advice on how to avoid having TOTO passed as the first variable?
You've discovered the answer yourself. This works fine
TOTO::print('Hello World');
If you call it as
TOTO->print('Hello World');
then you're asking for perl to call print
as a class method and pass ('TOTO', 'Hello World')
as parameters to the TOTO::print
subroutine
If TOTO
is just a bunch of subroutines then, as you found, just call TOTO::totosub
Upvotes: 1
Reputation: 126722
In the simplest terms, to create an object-oriented TOTO
module you must create a file TOTO.pm
that contains at least a constructor subroutine new
package TOTO;
sub new {
bless {};
}
sub print {
print "I am a TOTO object\n";
}
1;
That code must be saved in a file called TOTO.pm
that must match the package TOTO
name in the source
Then you may write a program, say main.pl
, that uses that module. For instance
use strict;
use warnings 'all';
use TOTO;
my $object = TOTO->new;
$object->print;
And then you have created a new TOTO
object that says what it is
If I run
$ perl main.pl
I get the output
I am a TOTO object
You will want to make this code more useful, and there are many variations on this theme, but those are the basics
Upvotes: 2
Reputation: 70782
Check differences between this:
TOTO::print("Hello World");
and
TOTO->print("Hello World");
which is not proper object notation, because TOTO
is just a string.
Syntaxe object->function(arguments)
whil pass object
as 1st argument, to be stored as $this
, for sample.
sub print {
my $this = shift @_;
print Dumper(@_);
}
May do the job (even if not blessed object).
Try this:
package TOTO;
use Data::Dumper;
sub new { return bless {}, shift; }
sub print {
my $self = shift @_;
if ( scalar $self =~ /=HASH\(/ ) {
print Dumper(@_);
} else {
print Dumper($self);
}
}
package main;
my $package = TOTO->new();
$package->print("Hello World");
TOTO::print("Hello World");
This could output:
$VAR1 = 'Hello World';
$VAR1 = 'Hello World';
And have a look at man perlobj
, man perlootut
and man perlmodlib
Upvotes: 0
Reputation: 9296
That's just how Perl's package system works. You need to handle this yourself in the sub being called. You can't change it prior to the call.
sub print {
# special variable __PACKAGE__ contains "TOTO"
if ($_[0] eq __PACKAGE__ || ref $_[0] eq __PACKAGE__){
shift; # throw away class/object
}
print Dumper(@_);
}
The ref $_[0]
part isn't technically needed, because you don't have a constructor in your class (you call the method on the class only, but it will just do the right thing if you ever do use objects without having to change anything later).
Upvotes: 1