Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behavior of a tied hash in perl, when asking for an arrayref

Tags:

perl

tie

I was trying to tie an hash (or hashref) in order of tracking variable usages.

Everything is working for simple cases, but when I tried to use my module on some real code I had this error:

hash- or arrayref expected (not a simple scalar, use allow_nonref to allow this)

I've replicated the error using the following code:

use Tie::Hash::Usages;
use JSON;

my @arr = (
    {
        key1 => "ac",
        key2 => 12,
        key3 => 12
    },        
);
my %tied_hash;


tie %tied_hash, 'Tie::Hash::Usages';

$tied_hash{key1} = \@arr;

my @val = $tied_hash{key1};
print encode_json(\@val)."\n\n"; #this works

print encode_json($tied_hash{key1}); #this doesn't

The same code works with a plain hash.

I'd need this to work also in the second case, the code base is huge and I don't want to change it or live with the doubt that something somewhere will not work in some particular case.

Usages.pm (simplified)

package Tie::Hash::Usages;
use strict;
use warnings;

use Tie::Hash;

use vars qw(@ISA);

@ISA = qw(Tie::StdHash);

sub TIEHASH {

    my ($class, $tracker, $filename) = @_;
    my %hash;

    bless \%hash, $class;

}

sub STORE {
    my ($self, $key, $val) = @_;
    $self->{$key} = $val;
}

sub DELETE {
    my ($self, $key) = @_;
    delete $self->{$key};

}

sub FETCH {
    my ($self, $key) = @_;
    return $self->{$key};
}

sub DESTROY {
    my $self = shift;
}
1;

perl version: v5.18.2

like image 491
kanly Avatar asked Nov 16 '15 15:11

kanly


2 Answers

Minimal demonstration:

use JSON::XS  qw( encode_json );
use Tie::Hash qw( );

our @ISA = 'Tie::StdHash';

{
   tie my %tied, __PACKAGE__;
   $tied{data} = { a => 1 };
   encode_json($tied{data});  # Exception: hash- or arrayref expected ...
}

JSON is a front-end for JSON::PP (default) or JSON::XS (if found). This is a problem with JSON::XS.

A lot of XS code doesn't handle magical variables (which is what $tied{EXPR} returns), and while JSON::XS has handled magical values since version 1.2, it doesn't for the value directly passed to encode_json.

This is an existing bug in JSON::XS that can be worked around as follows:

encode_json(my $non_magical = $tied{data})

Bug reported.

like image 150
ikegami Avatar answered Nov 09 '22 22:11

ikegami


Unable to replicate using the code given, so what you're providing doesn't seem to be representative of your actual situation. The only thing I see that's the tiniest bit off is this line:

my @val = $tied_hash{key1};

in which you're assigning a scalar (your stored arrayref) to an array. Perl handles this fine, assembling an array with the scalar as sole content, but if your actual use case involves something more complex (maybe something with sub prototypes involved), conceivably something might be going wrong there.

like image 23
chaos Avatar answered Nov 09 '22 22:11

chaos