tags:

views:

101

answers:

3

Hi,

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:

  1. Is it safe to assume that if ref() returns not undef that it will return the current package?

  2. 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?

  3. 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)
+7  A: 

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 a typeof 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) = @_;
Sinan Ünür
Edited question to reflect correct usage of ref().
NXT
my($self, $bar,$baz) = @_ is my standard style
NXT
+4  A: 

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.

Dave Sherohman
Indeed. Many things break when people break the social contract of the interface. :(
brian d foy
A: 

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';
}
Eric Strom