Reputation: 1915
I have method in a class that I need to make sure is only called on an object instance, and not as a class method.
I will probably do something like this:
# Edit: this is terrible, don't do this, it breaks inheritance.
sub foo
{
my ($self) = @_;
if (ref($self) ne __PACKAGE__) { return; }
...do stuff
}
But I'm thinking it will be more efficient to do this:
sub foo
{
my ($self) = @_;
if (not ref($self)) { return; }
...do stuff
}
Questions:
Is it safe to assume that if ref() returns not undef that it will return the current package?
I would ideally like to go back through and do something like this in all my methods for sanity checking. Is that a bad idea?
Is there a more perlish way to do what I want?
"Use moose" is not an acceptable answer in this case. However if you are compelled to say that, please tell me how moose makes this easy or more efficient. I might want to incorporate it into my own object system.
Thanks!
EDITED to reflect that ref never returns undef, only an empty string.
EDIT 2 Here's a follow up question. Someone below suggested using:
$self->isa(__PACKAGE__)
But won't that always succeed? Unless of course the caller does something really boneheaded like:
MyClass::MyMethod($ref_to_some_other_object)
Upvotes: 3
Views: 827
Reputation: 40152
you can also use the functional form of isa, that way you dont have to check to make sure $self is a reference. of course, the functional isa has the caveat that packages cant override isa, but i'm torn as to if that's a good or bad thing. in my own code, i usually do something like this, which i find has more useful calling semantics than UNIVERSAL::isa.
sub isa {UNIVERSAL::isa @_ > 1 ? shift : $_, @_}
.....
return unless isa $self => __PACKAGE__;
for (@objects) {
say $_->name if isa 'Package';
}
Upvotes: 0
Reputation: 46187
Is it safe to assume that if ref() returns not undef that it will return the current package?
No.
my $bar = Bar->new;
Package::Foo::foo($bar);
will lead to foo
putting $bar
into $self
and ref $self
will then return Bar
.
And, as already noted in the earlier answers, checking for the literal package name rather than testing isa
breaks inheritance anyhow.
Upvotes: 4
Reputation: 118148
First, you should probably croak
rather than silently doing nothing because, according to your specs, calling foo
as a class method is a breach of contract.
Second, just checking if the first argument is a reference is enough. Your method will fail if there is inheritance involved:
#!/usr/bin/perl
package A;
use Carp;
sub new { bless {} => shift }
sub foo {
croak "I am not in " . __PACKAGE__ unless __PACKAGE__ eq ref(shift)
}
package B;
use base 'A';
package main;
$x = B->new;
$x->foo;
C:\Temp> t I am not in A at C:\Temp\t.pl line 19
See also perldoc -f ref:
If the referenced object has been blessed into a package, then that package name is returned instead. You can think of
ref
as atypeof
operator.
So:
sub foo {
croak "Don't call as class method" unless ref shift;
}
Finally, note that ref
never returns undef
.
Is it a good idea to add this check to every method? I guess one could make that argument from a design by contract view.
On the other hand, my methods assume they were called as instance methods and I only check for the possibility of a method being called as a class method if the method can provide a meaningful alternative behavior when called as such.
I cannot remember any modules which have these kinds of checks either.
By the way, instead of
sub foo {
my ($self) = @_;
you should use
sub foo {
my $self = shift;
leaving only the arguments to the method in @_
to be unpacked. Or, you should unpack all arguments in one fell swoop:
sub foo {
my ($self, $bar, $baz) = @_;
Upvotes: 7