简体   繁体   中英

In Perl how can I save a hash that contains subroutine references?

As the title says, in Perl, how can I save a hash that contains a list of subroutine references? For example, I have the following hash that contains the references to the subroutines which are contained in other libraries:

my %testMap = (
  helloTest        => \&runHello,
  goodbyeTest      => \&runGoodbye,
);

When I try to use Data::Dumper in the following matter:

my($out) = new FileHandle ">$fileName"; 
my $serialized => Data::Dumper->Dump([\%testMap], [$HASH_REFERENCE]); 
print $out $serialized; 
close $out;

I end up with a file that looks like the following:

$testMap = {
             'goodbyeTest' => sub { "DUMMY" },
             'helloTest' => sub { "DUMMY" }
           };

When I would like the output to look like what appears in the original listing, is there a way to do this?

Some experimentation with Data::Dumper and Storable have so far turned up nothing and I suspect that it is due to the actual code for the references not being available to the code that is running.

Storable has been able to serialize coderefs since version 2.05.

use strict;
use warnings;
use Storable;
use Data::Dump 'dump';

{
    no warnings;             # Suppress 'used only once' warning
    $Storable::Deparse = 1;  # Needs to be set to true as per docs
    $Storable::Eval    = 1;       # Same as above
}

sub hello_world { print "Hello world!\n" }

my %hash = (
             helloTest => \&hello_world,
             byeTest   => sub { print "Goodbye!\n" },
           );

store \%hash, 'file';            # Could use freeze/thaw for
my $cloned = retrieve( 'file' ); # in-memory serialization

$cloned->{helloTest}();          # Prints 'Hello world!'

Set $Data::Dumper::Deparse to a true value.

This uses B::Deparse to reconstruct the source from op codes. This usually, but not always, works.

$ perl -MData::Dumper -e 'sub foo { print "Hello world" };' \
>     -e  '$Data::Dumper::Deparse=1; print Dumper \&foo'
$VAR1 = sub {
            print 'Hello world';
        };

If you want to parse a Perl source code file and extract the text of the subroutines, that's a very different problem. But there's a package for that , too:

# quick and dirty sub extractor
use PPI;
use Data::Dumper;
$doc = PPI::Document->new( "your_source_code_file_name" );
foreach $sub ( @{$doc->find( 'PPI::Statement::Sub' )} ) {
  @t = $sub->tokens;
  $name = $t[2];
  $code = "sub " . join q//, @t[3..$#t];
  $teh_codez{$name} = $code;
}    
print Data::Dumper::Dumper \%teh_codez;

First, Data::Dumper is a debugging tool, not a serialiser. It's not all that good at doing the latter. At the very least, be sure to set the Purity option. That'll make it work in some of the case where it wouldn't otherwise work. There are still issues with it breaking aliases, though.

Storable should be used to non-trivial data, and I'd use JSON::XS or a YAML module for trivial data.

Second, what you want can be attempted by setting the Deparse option.

use Data::Dumper qw( Dumper );

my $serialised;
{
   local $Data::Dumper::Purity  = 1;
   local $Data::Dumper::Deparse = 1;
   $serialised = Dumper($struct);
}

It will fail for closures, and XS functions and it doesn't capture pragmas in effect.

my $struct = { f => do { my $x = 123; sub { $x } } };

produces

$VAR1 = {
          'f' => sub {
                     $x;
                 }
        };

I suggest that you store the hash with the names of the subroutines to be called, and then resolve them to subroutine references after retrieval

This code shows the idea

use strict;
use warnings;

my %testMap = (
  helloTest   => 'runHello',
  goodbyeTest => 'runGoodbye',
);

$testMap{$_} = \&{$testMap{$_}} for keys %testMap;

$testMap{helloTest}();
$testMap{goodbyeTest}();

sub runHello {
  print "runHello\n";
}

sub runGoodbye {
  print "runGoodbye\n";
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM