1

Following this question, I used the answer there (posted here too) and now I'm getting a failure. I understand that the failure probably comes from the line "return bless $self->merge($left, $right), $class_left;", but I don't understand what could be the problem.

My code:

#!usr/bin/perl
use strict;
use warnings;
use Hash::Merge;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
use Data::Structure::Util qw(unbless);


my $hash1 = bless( {
    'Instance' => {
        'pipe_2' => {
            'LineNumber' => bless( do{\(my $o = '200773952')}, 'Veri::ColLineFile' )
        }
    },
}, 'IB' );

my $hash2 = bless( {
    'Instance' => {
        'pipe_2' => {
            'LineNumber' => bless( do{\(my $o = '200773952')}, 'Veri::ColLineFile' )
        }
    },
}, 'IB' );

my $merger = Hash::Merge->new('LEFT_PRECEDENT');
my $behavior = $merger->get_behavior_spec($merger->get_behavior);
my $old_behavior_scalar_scalar = $behavior->{SCALAR}{SCALAR};
$behavior->{SCALAR}{SCALAR} = sub {
    my $self  = &Hash::Merge::_get_obj;
    my ($left, $right) = @_;
    my ($class_left, $class_right) = (ref $left, ref $right);
    print("left = $left, class_left = $class_left right = $right, class_right = $class_right \n");  # I ADDED THIS LINE FOR DEBUGGING
    if ($class_left && $class_left eq $class_right) {
        unbless $left;
        unbless $right;
        return bless $self->merge($left, $right), $class_left;
    } else {
        # Regular scalars, use old behavior
        return $old_behavior_scalar_scalar->($left, $right);
    }
};
my $hash3 = $merger->merge($hash2, $hash1);

print Dumper($hash3);

Output:

Deep recursion on subroutine "Hash::Merge::merge" at ../rrr line 40.
Deep recursion on anonymous subroutine at ...../freeware/cpan/5.18.4/1/el-7-x86_64/lib/perl5/Hash/Merge.pm line 227.

and after adding the debugging line:

left = SCALAR(0x2db6d70), class_left = SCALAR right = SCALAR(0x2db6d88), class_right = SCALAR
left = SCALAR(0x2db7268), class_left = SCALAR right = SCALAR(0x2db7280), class_right = SCALAR
left = SCALAR(0x2db7760), class_left = SCALAR right = SCALAR(0x2db7778), class_right = SCALAR
left = SCALAR(0x2db9e40), class_left = SCALAR right = SCALAR(0x2db9e58), class_right = SCALAR
left = SCALAR(0x2dba338), class_left = SCALAR right = SCALAR(0x2dba350), class_right = SCALAR
left = SCALAR(0x2dba830), class_left = SCALAR right = SCALAR(0x2dba848), class_right = SCALAR
left = SCALAR(0x2dbad28), class_left = SCALAR right = SCALAR(0x2dbad40), class_right = SCALAR
.... #endless lines

*** AFTER EDIT: ***

This case (mysteriously) does work.

my $hash1 = bless( {
    'Instance' => {
        'pipe_2' => {
            'veri_id' => [
                bless( do{\(my $o = '201142064')}, 'Verific::VeriIdDef' )
            ]
        }
    },
}, 'IB' );

my $hash2 = bless( {
    'Instance' => {
        'pipe_2' => {
            'veri_id' => [
                bless( do{\(my $o = '201142064')}, 'Verific::VeriIdDef' )
            ]
        }
    },
}, 'IB' );
Jim Davis
  • 5,241
  • 1
  • 26
  • 22
urie
  • 361
  • 2
  • 14
  • *"return bless $self->merge($left, $right), $class_left;"* : Here you are calling `merge()` recursively and it will never return since `$left` and `$right` are still scalar references – Håkon Hægland Sep 09 '21 at 09:09
  • hmm.. by investigating this a little bit further it seems that the problem is related to the sub-hash with field `LineNumber` which is blessed into a reference to an integer string – Håkon Hægland Sep 09 '21 at 09:17
  • I know that that's the problem, but I can't understand what is the problem with that line. When there's no blessing inside, it works fine. I edited the question with another example that works. – urie Sep 09 '21 at 09:23
  • 1
    Maybe you could add a check if the `$left` and `$right` are blessed before you do the recursive call? As it is now you only check if `ref` does not return `undef`, but that does not mean that the reference is blessed.. See [Scalar::Util::blessed](https://metacpan.org/pod/Scalar::Util#blessed) for how to check if a ref is blessed – Håkon Hægland Sep 09 '21 at 09:30
  • @HåkonHægland That worked, but it still doesn't merge it correctly, I'm trying to figure out why. – urie Sep 09 '21 at 12:05

1 Answers1

3

The issue is that unbless unblesses all object within its argument recursively. Quoting its documentation:

Note that the structure looks inside blessed objects for other objects to unbless.

In your example, your 2 objects are blessed, and they each contain an internal blessed object. After doing unbless $left, both blesses are removed, and you can never recover the internal one.

To fix this, you can write your own implementation of unbless as follows (assuming that typeglob do not have to be handled, for simplicity):

sub unbless {
    my $r = eval { ${$_[0]} };
    return \$r unless $@;
    $r = eval { [ @{$_[0]} ] };
    return $r unless $@;
    $r = eval { +{ %{$_[0]} } };
    return $r unless $@;
    die "Unable to unbless.";
}

The idea of this function is that you can dereference a blessed reference like you would an unblessed one, and then you can take the reference of the dereferenced object, which won't be blessed. Except that to do that, you need to know the underlying type of the reference (scalar, arrayref, hashref). The function unbless above tries all of them with eval, and return the one that works.

Note that instead of modifying its argument, it returns an unblessed equivalent. This means that you need to do $left = unbless $left instead of unbless $left. Also, don't forget to remove use Data::Structure::Util.

There is a second issue with your current code: it does not handle scalar references, one which it will loop forever. You can fix that by adding simple check for that case:

$behavior->{SCALAR}{SCALAR} = sub {
    my $self  = &Hash::Merge::_get_obj;
    my ($left, $right) = @_;
    my ($class_left, $class_right) = (ref $left, ref $right);
    print("left = $left, class_left = $class_left right = $right, class_right = $class_right \n");  # I ADDED THIS LINE FOR DEBUGGING
    if ($class_left && $class_left eq $class_right) {
        if ($class_left eq 'SCALAR') {
            return \($self->merge($$left, $$right));
        } else {
            $left = unbless($left);
            $right = unbless($right);
            return bless $self->merge($left, $right), $class_left;
        }
    } else {
        # Regular scalars, use old behavior
        return $old_behavior_scalar_scalar->($left, $right);
    }
};
Dada
  • 6,313
  • 7
  • 24
  • 43
  • Hi, I don't understand what is the point of "my $unblessed" - Where does it take place? – urie Sep 09 '21 at 12:12
  • It works! :) For some reason not on my code (Maybe I'll open another question if needed...) Could you explain the first function again? What does the hash/list/scalar have to do with the blessing? I thought blessing is like a struct/class... How does your "unbless" code above solve it? – urie Sep 09 '21 at 12:43
  • @urie Regarding `my $unblessed`: it was left-over from a previous version of this function; I've removed it; it did not serve any purpose. – Dada Sep 09 '21 at 15:06
  • @urie Regarding your other comment: read [this answer](https://stackoverflow.com/a/392194/4990392) to know a bit more about bless ;) – Dada Sep 09 '21 at 15:07