Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl: When called in a method, can ref($self) ever return anything other than __PACKAGE__ or undef?

Tags:

oop

perl

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)
like image 351
NXT Avatar asked Jan 01 '26 18:01

NXT


2 Answers

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) = @_;
like image 52
Sinan Ünür Avatar answered Jan 05 '26 00:01

Sinan Ünür


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.

like image 31
Dave Sherohman Avatar answered Jan 04 '26 23:01

Dave Sherohman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!