http://www.perlmonks.org?node_id=279780

skyknight has asked for the wisdom of the Perl Monks concerning the following question:

I have been tasked with the odious chore of digging through someone else's incredibly complicated tool and associated library structure. Just now I am staring at a piece of code that takes the form...

do_a_whole_bunch_of_stuff() if $obj1 eq $obj2;

These scalars are not primitive strings, so clearly for this statement to be of any use, someone, somewhere in the inheritance hierarchy has overloaded the 'eq' operator. Now, in theory, I could go digging my way through the whole inheritance tree and try to find the "use overload" statement that is triggering the behavior, but that seems rather brutish. Another idea I had was to fire up the debugger and actually perform such a comparison, making note of the location to which execution jumped. This seems more elegant, though I can't help but wonder if there is a less hackish way.

One can perform code reflection on an object by poking around in the classes from its inheritance hierarchy, so as to deduce what methods are defined. Is there a similar paradigm that I might follow regarding operator overloading?

Replies are listed 'Best First'.
Re: Clawing my way out of operator overloading hell
by Elian (Parson) on Jul 31, 2003 at 20:52 UTC
    No overloading is required. That'll do an identity comparison of the two variables and return true if they're the exact same object. Someone probably noticed that a ref to an object (or any other thing for that matter) has a predictable string value and two references to the same thing will stringify identically.

    FWIW, it's inefficient--a numeric comparison (==) is significantly faster, which can be handy to know if you're doing this comparison a lot.

Re: Clawing my way out of operator overloading hell
by ViceRaid (Chaplain) on Aug 01, 2003 at 12:58 UTC

    To answer your original question: you can use the Method function in the overload package to get a reference to the subroutine that implements a given operator for an overloaded class; eg:

    my $thing = OverloadingClass->new(); my $subref = overload::Method($thing, 'eq');

    You could then use something like Devel::Peek to discover the original package that implemented the subroutine reference that was returned. See the CvGV ("Code-Value-to-Glob-Value") method there. Here's a slightly inane example:

    package MyOverLoaded; use overload 'eq' => \&my_equal; # just an example overloaded implementation of eq sub my_equal { my $self = shift; my $comparator = shift; return length($$self) == length($comparator); } sub new { my $class = shift; my $thing = shift; bless(\$thing, $class); } package OverLoadedSubClass; use base qw(MyOverLoaded); package main; use Devel::Peek qw(CvGV); my $thing = OverLoadedSubClass->new("It's my birthday!"); my $sub = overload::Method($thing, 'eq'); print CvGV($sub); # prints "*MyOverLoaded::my_equal"

    Cheers
    ViceRaid

      Yee-haw! Most of the other replies I got were interesting and insightful, but weren't quite the answers for which I was looking. Your reply explains exactly the kind of thing that I had hoped existed. Thanks a lot.
Re: Clawing my way out of operator overloading hell
by sauoq (Abbot) on Jul 31, 2003 at 20:54 UTC
    These scalars are not primitive strings, so clearly for this statement to be of any use, someone, somewhere in the inheritance hierarchy has overloaded the 'eq' operator.

    Regardless of whether or not that is true in your case, it isn't a foregone conclusion in every case. If you know that two scalars contain references, a string comparison will tell you whether or not they refer to the same thing.

    $ perl -le 'my $foo = \1; my $bar = $foo; print "same" if $foo eq $bar +' same
    -sauoq
    "My two cents aren't worth a dime.";
    
      While the string eq operator can check for reference equivalence, the numeric == operator is preferred for this sort of check. It seems that not many people note this message in perlref:
        Using a string or number as a reference produces a symbolic reference, as explained above. Using a reference as a number produces an integer representing its storage location in memory. The only useful thing to be done with this is to compare two references numerically to see whether they refer to the same location.

      By using the == instead of eq, you compare the addresses directly, rather than converting both operands to strings which happen to include identical character sequences.

      They're functionally equivalent today, but I imagine someone may find some odd exploit involving stringified references. While I don't think Perl5 could possibly fix stringified references without breaking the semantic assumptions made in a metric buttload of scripts, it's good to get out of the habit of measuring proxied symptoms and instead use the proper operator.

      --
      [ e d @ h a l l e y . c c ]

        While the string eq operator can check for reference equivalence, the numeric == operator is preferred for this sort of check.

        Sure. It is much more efficient. I only intended to let the OP know that the code he is maintaining might be doing something useful without operator overloading. (It turns out that I guessed right.)

        By using the == instead of eq, you compare the addresses directly, rather than converting both operands to strings which happen to include identical character sequences.

        Well, I wouldn't go as far as to say stringified references just "happen to include identical character sequences." They include identical character sequences because the same code produces those characters from the same input data.

        They're functionally equivalent today, but I imagine someone may find some odd exploit involving stringified references.

        An exploit? I would love to hear more about how you think someone might accomplish that. :-)

        Yes, == is better than eq for checking reference equality. It's much faster. Use numerical comparison. Tell others to use numerical comparison. But, there is no reason to be paranoid about the construct. It isn't dangerous. It isn't deprecated. (afaik) It works fine; it's just slow.

        P.S. I can even think of one good reason to use it. What if you have overridden '0+' and want to check for reference equality rather than the equality of two objects' overridden numification?

        -sauoq
        "My two cents aren't worth a dime.";
        
Re: Clawing my way out of operator overloading hell
by chunlou (Curate) on Aug 01, 2003 at 01:37 UTC
    I once used Aspect. I was trying to trace the double quotes operator (that's what's being called implicitly when using print). The following is an example.
    use strict; use warnings; # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - use Aspect qw(advice calls returns around) ; my $aspect = advice( calls(qr/^(Quantum|main)::(.*)/), sub { printf "calling -> %s\n", $::thisjp->sub } ); $aspect->enable; # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - use Quantum::Entanglement; sub foo { my $die = entangle( 1=>1, 1=>2, 1=>3, 1=>4, 1=>5, 1=>6 ); print "$die" ; # observed state print "\n" ; } foo(); __END__ calling -> main::foo calling -> main::entangle calling -> Quantum::Entanglement::_new calling -> Quantum::Entanglement::("" calling -> Quantum::Entanglement::_normalise 3 calling -> Quantum::Entanglement::DESTROY calling -> Quantum::Entanglement::_rationalise_states calling -> Quantum::Entanglement::_unravel
      Interesting. Both your reply and ViceRaid's below are the kind of answers for which I was searching. In fact, it seems that the other comment-makers are right, in that the code was comparing two references directly, but this has been a good, all-around learning experience regardless. Fun stuff.
Re: Clawing my way out of operator overloading hell
by graff (Chancellor) on Aug 01, 2003 at 04:15 UTC
    The first two replies above (Elian and sauoq) are fine for the situation where you may be handling multiple references to the same instance of an object. But there may also be the case where two independent data structures (two separate instances of the same type of object) are created and populated, and happen to end up containing identical data values. Just comparing the object handles won't detect this sort of identity.

    I wonder whether Data::Dumper might provide a useful shortcut in this case (I haven't had occasion to use it myself yet) -- if nothing else, you may be able pass the Dumper's output for each object to Digest::MD5, and see whether the two objects have identical signatures.

    update: Having just read the first paragraph of the OP more carefully, I realize that I missed the point and my remarks are not relevant (interesting, maybe, but not relevant). Elian and sauoq are right -- whoever wrote the original code was simply checking to see whether $obj1 and $obj2 were both references to the same instance of an object.

    (one more update: since you're trying to scope out someone else's complicated, multi-file perl code, I could put in a plug for a tool I posted here to tabulate subroutines in multiple related perl files (calls and definitions). Hope it helps.

      Further to your interesting but irrelevant remarks, if you want to compare 2 deep data structures, you can use Test::More's is_deeply() or Test::Deep's cmp_deeply(). is_deeply currently has a few bugs, it can't handle circular structures (fixes are in the pipeline). cmp_deeply has no bugs (cough!) and can do all sorts of weird and wonderful things (hence the need for a new module).

      Both of these modules will give you nice diagnostics about where the structures differ.

Re: Clawing my way out of operator overloading hell
by fglock (Vicar) on Jul 31, 2003 at 20:58 UTC

    I'd look for methods called "as_string", "stringify", "cmp" or "compare".

    Another way would be to add:

    warn "comparing $obj1 and $obj2";