Re: How to differentiate hash reference and object reference?
by jdhedden (Deacon) on Aug 31, 2005 at 11:59 UTC
|
use strict;
use warnings;
use Scalar::Util 'blessed';
wants_foo_object({});
sub wants_foo_object {
die "no object" unless defined(my $obj = shift);
die "not an object" unless blessed($obj);
die "not a foo" unless $obj->isa('foo');
}
Remember: There's always one more bug.
| [reply] [d/l] [select] |
Re: How to differentiate hash reference and object reference?
by tlm (Prior) on Aug 31, 2005 at 11:59 UTC
|
Check out Scalar::Util::blessed. You can also use UNIVERSAL::isa( $obj, 'foo' ).
| [reply] |
|
|
Be very careful calling UNIVERSAL::isa as a function, though, and be sure that's what you want. It's nice in that you can throw any reference at it whether or not it's blessed, but it doesn't allow various facade or delegation patterns that don't rely on @ISA inheritance, but do override isa to signal that they do, indeed, subclass an object through other means. This is probably better (using blessed as recommended):
blessed $obj && $obj->isa('foo')
-xdg
Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.
| [reply] [d/l] [select] |
Re: How to differentiate hash reference and object reference?
by Arunbear (Prior) on Aug 31, 2005 at 13:10 UTC
|
Are you doing this in a test suite as the .t suggests? Then use Test::More:
use warnings;
use strict;
use Test::More qw(no_plan);
my $foo = {};
isa_ok($foo, 'My::Object');
which gives:
not ok 1 - The object isa My::Object
# Failed test (temp.pl at line 6)
# The object isn't a 'My::Object' it's a 'HASH'
1..1
# Looks like you failed 1 test of 1.
| [reply] [d/l] [select] |
|
|
No, I'm thinking more, help users of the module debug fast and obviously. Good tip though.
| [reply] |
Re: How to differentiate hash reference and object reference?
by demerphq (Chancellor) on Aug 31, 2005 at 13:19 UTC
|
I have to say that I wonder about validation code like this. Whats the point? You are checking the type to throw an error, but what will happen if you try to use the object without doing these checks? Remarkably, in almost every way the exact same thing will happen (under strict anyway), ie, the interpreter will throw an errror.
So just let the interpreter throw the error.
Now I can hear the peanut gallery saying "yeah but what happens if they pass in an object that is blessed into a package that has the methods im concerned about?" My answer would be, you mean you know in advance that this would be wrong? What happens if they decide to reimplement the interface in a different class structure and decide to use that?
IE, most of this type of validation just removes the flexibility of the consumer, and adds very little or nothing to the robustness of your code.
I will agree there are cases where doing checks like these properly is vital, but IMO that is usally confined to cases where you have specific handlers for different input types, not simply screen for a valid type.
Ultimately if you _do_ want to do checks like these then you should look into using Scalar::Util.
---
$world=~s/war/peace/g
| [reply] |
|
|
The thing is, I want to be able to control my error messages. Right now, I have
#The error I get:
#C:\thomasdata\experiments>perl unblessedWantBetterError.t
#Can't call method "isa" on unblessed reference at unblessedWantBetter
+Error.t lin
#e 10.
It is an error message, but it comes from the compiler, not from me. My main problem with it is isn't so much the wording, but the fact that it doesn't even say which argument caused the problem. Maybe params validate solves that?
That said, I am overall not currently happy with how I'm going about params checking. I will have a look at Params::Validate like diotalevi said, and maybe give my whole way of doing this a rethink. | [reply] [d/l] |
|
|
My main problem with it is isn't so much the wording, but the fact that it doesn't even say which argument caused the problem.
Sure it does. the message says
Can't call method "isa" on unblessed reference at unblessedWantBetterError.t line 10.
Which tells us that on line 10 some $var is being used as an object in a method call and the content of $var is not actually an object. When we look at line 10 we see
die "not a foo" unless $obj->isa('foo'); #the error I want
which tells us that the $var in question is called $obj.
Now presumably you are doing this because later on you have code that does something like
$obj->do_something($useful);
But what do you gain? In this situation you would see the same error you are now just on the actual line where the actual code is being used, and not on some validation code.
So IMO you gain nothing but code complexity and you tie the hands of your codes consumers because they can't drop in a different object with a compatible interface. So IOW, you have made your code less usable and robust than more.
---
$world=~s/war/peace/g
| [reply] [d/l] [select] |
|
|
Re: How to differentiate hash reference and object reference?
by davis (Vicar) on Aug 31, 2005 at 12:02 UTC
|
| [reply] [d/l] |
|
|
No, don't do that. ref is broken:
- It fails for certain valid invocants (class names).
- It prohibits polymorphism, both in inheritance and interface equivalence.
- It returns undef on failure, so you have to go through contortions to avoid undefined value warnings.
- It's trivially easy to fool by blessing into a package named HASH.
perlfunc in bleadperl recommends several other techniques for doing the same thing without the problems.
| [reply] [d/l] [select] |
|
|
You can also fool ref() by blessing your objects into the "\0" package. In that case, you'd get a false value on something that is not only a ref but is even blessed.
| [reply] |
|
|
| [reply] |
Re: How to differentiate hash reference and object reference?
by holli (Abbot) on Aug 31, 2005 at 12:44 UTC
|
From perlfunc:
ref EXPR
ref
Returns a true value if EXPR is a reference, false otherwise. If EXPR is not specified, $_ will be used. The value returned depends on the type of thing the reference is a reference to. Builtin types include:
SCALAR
ARRAY
HASH
CODE
REF
GLOB
LVALUE
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, there's no need to call ->isa():
sub wants_foo_object
{
my $obj = shift;
die unless $obj && ref($obj) eq "foo";
#...
}
(untested)
| [reply] [d/l] |
|
|
| [reply] |
|
|
No, isa() returns true if whatever method isa() is for the invocant it returns true. If I wrote an object and overrode isa() to return true based on a the phase of the moon, that's what it would do.
That's probably unwise code to write, but the purpose of having isa() be a method is so that classes can override it when it makes sense to do so.
| [reply] [d/l] [select] |
|
|
Re: How to differentiate hash reference and object reference?
by diotalevi (Canon) on Aug 31, 2005 at 12:48 UTC
|
sub wants_foo_object {
my ( $foo ) = validate_pos( @_,
{ class => 'foo' } );
....
}
| [reply] [d/l] |
|
|
| [reply] |
|
|
That's not what I want to see on this site.
And it's absolutely not what we are used to from diotalevi. I wonder what has bitten him.
| [reply] |