Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Determining the true type of a reference

by Mr. Muskrat (Canon)
on May 24, 2003 at 16:29 UTC ( [id://260606]=perlquestion: print w/replies, xml ) Need Help??

Mr. Muskrat has asked for the wisdom of the Perl Monks concerning the following question:

theorbtwo got me to thinking about improperly blessed references (blessed into classes they shouldn't be in) with his post here. How do you determine the true type of a reference? "Use UNIVERSAL::isa!" Right? Wrong!

Take the following (contrived) snippet for example.

my @array = (1, 2, 3); my $aref = \@array; showarray($aref); my $text = 'some text'; my $ref = \$text; my $blessed = bless($ref, 'ARRAY'); # bless the scalar reference into +class ARRAY showarray($blessed); sub showarray { my $aref = shift; local $\ = $/; # set the output record seperator return if (!UNIVERSAL::isa($aref, 'ARRAY')); print ref($aref), ": @$aref"; } __DATA__ ARRAY: 1 2 3 Not an ARRAY reference at reftype.pl line 14.
Oops! How'd a scalar reference sneak by? (We really wanted to print $$aref; in this case.)

For one thing, you can use B::svref_2object (thanks diotalevi!):

use B; sub refcheck { my $ref = shift; return ref(B::svref_2object($ref)) =~ /B::(\w+)/; }
This will return the underlying C structure type but there are problems with this method (at least for me). Every time I try to use one of the B::C_Structure methods it results in a core dump. (B::PVMG::SvSTASH($object), B::PVLV::TYPE($lref), etc...)

You could simply strip off the excess information:

sub reftype { my $ref = shift; $ref =~ s/\(.*\)$//; # get rid of the (0x0000000) portion of the ref +erence $ref =~ s/^.*=//; # get rid of the CLASS= portion of the referenc +e return $ref; }
Although I'm sure this has some major drawbacks as well.

And this brings me back to my original question. How do you determine the true type of a reference?

Replies are listed 'Best First'.
Re: Determining the true type of a reference
by adrianh (Chancellor) on May 24, 2003 at 16:32 UTC
      I searched CPAN for 'reference' but Scalar::Util didn't come up in the results. Wow. This works great. Thank you!
Re: Determining the true type of a reference
by theorbtwo (Prior) on May 24, 2003 at 20:09 UTC

    I used UNIVERSAL::isa for reasons that often apply:

    1. Scalar::Util is fairly recent, meaning that many people don't have it.
    2. UNIVERSAL::isa will work just fine unless other code is being either amazingly stupid or amazingly devious. In either case, you're getting what you deserve if you wrote the code that blesses into ARRAY (etc).


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

      I have a tendency to be amazingly stupid quite often and every now and then amazingly devious. ;-)
Re: Determining the true type of a reference
by PodMaster (Abbot) on May 25, 2003 at 06:42 UTC
Re: Determining the true type of a reference
by Aristotle (Chancellor) on May 25, 2003 at 00:14 UTC
    That has a drawback when stringification is overloaded. The logical next step is to take that into account:
    sub reftype { require overload; local $_ = overload::AddrRef(shift); return /=(\w+(?:::\w+)*)\(0x[0-9a-f]\)\z/; }

    I think you can put things into the package tables at runtime that won't match \w+(?:::\w+)*, but I can't imagine any sane use of that so I'd document it as a limitation and move on.

    Update: substituted a star for the plus quantifier. Thanks theorbtwo.

    Makeshifts last the longest.

Re: Determining the true type of a reference
by bobn (Chaplain) on May 25, 2003 at 16:02 UTC
    Maybe I'm just slow today. I don't even understand the question. You think that a reference of type ARRAY is the same as membership in the (poorly named) Class ARRAY? This seems nonsensical on it's face. Two entirely different things.

    ref did what the doc says it does. So did isa.

    I think the simple answer is "Don't name your classes after predefined types". Then you don't have this issue.

    We have enough real porblems here without going out of our way to confuse ourselves.

    Bob Niederman, http://bob-n.com
      I'm not sure I actually responded to the real question. I still think naming a class 'ARRAY' is begging for trouble.

      But here a snippet that knows the difference:
      $aref = []; $href = {}; $sref = \""; my $text = 'some text'; my $ref = \$text; my $blessed = bless($ref, 'ARRAY'); print prt_typ($_) for ( [ $aref, '$aref'], [ $href, '$href'], [ $sref, '$sref'], [$blessed, '$blessed'], ); sub prt_typ { my ( $rin, $str ) = @{$_[0]}; print "ref($str) = ", ref($rin), "\n"; eval { my $x = ${$rin} }; return "$str is SCALAR\n\n" unless $@; eval { my %x = %{$rin} }; return "$str is HASH\n\n" unless $@; eval { my @x = @{$rin} }; return "$str is ARRAY\n\n" unless $@; }
      Results in:
      ref($aref) = ARRAY $aref is ARRAY ref($href) = HASH $href is HASH ref($sref) = SCALAR $sref is SCALAR ref($blessed) = ARRAY $blessed is SCALAR



      Bob Niederman, http://bob-n.com

        Unless we bring overloading in :-). Consider:

        { package FakeScalar; use overload '${}' => sub { \(shift->{scalar}) }; sub new { bless { scalar => undef }, shift }; }; use Scalar::Util qw(reftype); my $scalar = FakeScalar->new; print "reftype(FakeScalar) = ", reftype($scalar), "\n"; print prt_typ( [ $scalar => 'FakeScalar'] ); __END__ # produces reftype(FakeScalar) = HASH ref(FakeScalar) = FakeScalar FakeScalar is SCALAR
Core Dump using B::XXXX::METHODS resolved
by Mr. Muskrat (Canon) on May 26, 2003 at 15:15 UTC

    I figured out why the B::XXXX::METHODS were causing page faults. It was a case of user error. <sheepish grin>

    One must pass an object from the appropriate class to them or that will happen. For example, to use B::PVLV::TYPE you have to pass an object of the B::PVLV class to it.

    use B; my $lvalue = \substr("abc",1,1); my $lvobject = B::svref_2object($lvalue); print B::PVLV::TYPE($lvobject);

    Had I read the item about B::svref_2object I would have known this.

      No, you just called the method incorrectly. $lvobject->TYPE should work regardless of what sort of thing was handed to B::??::TYPE. So what made you think you should call a method like a function? Normally you call a method like a method and then any inheritance (what B:: uses) or any class AUTOLOAD can handle the issue. So what gives?

      I was off at a great party over the weekend and didn't see your post until just now. :-)

        Do I have egg on my face?
        Looks like I should reread the whole set of B docs.

Re: Determining the true type of a reference
by little (Curate) on May 24, 2003 at 17:13 UTC

    ref

    Have a nice day
    All decision is left to your taste

      +=0 (you meant well)

      That won't work little. Try changing this line in showarray: print ref($aref), ": @$aref"; to

      print ref($aref); print "@$aref";
      and you will see that it outputs 'ARRAY' which is wrong. In this case, $aref is a SCALAR reference that is blessed into the class ARRAY.

        that won't work due to your own belssing I assume
        my $blessed = bless($ref, 'ARRAY'); # bless the scalar reference into class ARRAY

        Have a nice day
        All decision is left to your taste

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://260606]
Approved by broquaint
Front-paged by broquaint
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (5)
As of 2024-04-24 03:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found