NAME
Data::Decycle - (Cyclic|Circular) reference decycler
VERSION
$Id: Decycle.pm,v 0.2 2010/08/23 09:11:03 dankogai Exp dankogai $
SYNOPSIS
use Data::Decycle;
# none of them leak
{
my $guard = Data::Decycle->new;
add $guard my $cyclic_sref = \my $dummy;
$cyclic_sref = \$cyclic_sref;
add $guard my $cyclic_aref = [];
$cyclic_aref->[0] = $cyclic_aref;
add $guard my $cyclic_href = {};
$cyclic_href->{cyclic} = $cyclic_href;
}
# or register all at once
{
my $guard = Data::Decycle->new(
my $cyclic_sref = \my $dummy,
my $cyclic_aref = [],
my $cyclic_href = {}
);
$cyclic_sref = \$cyclic_sref;
$cyclic_aref->[0] = $cyclic_aref;
$cyclic_href->{cyclic} = $cyclic_href;
}
Code Reference and PadWalker
If you have PadWalker, you can decycle closures, too.
{
my $guard = Data::Decycle->new;
my $cref;
$cref = sub{ $_[0] <= 1 ? 1 : $_[0] * $cref->($_[0] - 1) };
$guard->add($cref);
print $cref->(10);
}
Functional Interface
You can also cope with circular references explicitly
use Data::Decycle ':all';
my $obj = bless {}, 'Dummy';
$obj->{me} = $obj;
print may_leak($obj); # true
weaken_deeply($obj);
print may_leak($obj); # false
print has_cyclic_ref($obj); # true
decycle_deeply($obj);
print $obj->{me} == undef; # true
as a base class
You can also use it as a base class.
{
package Dummy;
use base 'Data::Decycle';
sub new { bless $_[1], $_[0] }
sub DESTROY { warn "($_[0])" }
}
{
my $mom = Dummy->new( {} );
my $son = Dummy->new( {} );
say "($mom) has cyclic ref ? ", $mom->has_cyclic_ref ? 'yes' : 'no';
say "($son) may leak ? ", $son->may_leak? 'yes' : 'no';
$mom->{son} = $son;
$son->{mom} = $mom;
say "($mom) has cyclic ref ? ", $mom->has_cyclic_ref ? 'yes' : 'no';
say "($son) may leak ? ", $son->may_leak? 'yes' : 'no';
$mom->weaken_deeply;
$son->weaken_deeply;
say "($mom) has cyclic ref ? ", $mom->has_cyclic_ref ? 'yes' : 'no';
say "($son) may leak ? ", $son->may_leak? 'yes' : 'no';
}
DESCRIPTION
Perl programmers love to hate cyclic References, or circular references. It easly leaks out of perl's reference-counter based garbage collection and stays there until perl exits.
Even with the introduction of weak references in Perl 5.8, you still have to tell perl explicitly to weaken references and which reference to weaken is tricky.
use Devel::Peek;
use Scalar::Util qw/weaken/;
my $obj = {you => $ENV{USER}};
$obj->{me} = $obj;
weaken($obj); # wrong
weaken($obj->{me}) # right;
In addition to that, weak references do not work with code references.
my $cref;
$cref = sub { $_[0] <= 1 ? 1 : $_[0] * $cref->( $_[0] - 1 ) };
print $cref->(10);
weaken($cref); # does undef($cref)
print $cref->(10); # goodbye
This module offers something easier than that.
HOW DOES IT WORK?
See the source :-p
Okay, I'll be nicer. Consider the code below again.
{
my $guard = Data::Decycle->new(
my $cyclic_sref = \my $dummy,
my $cyclic_aref = [],
my $cyclic_href = {}
);
$cyclic_sref = \$cyclic_sref;
$cyclic_aref->[0] = $cyclic_aref;
$cyclic_href->{cyclic} = $cyclic_href;
}
What happens when it reaches out of the block? $guard will surely be DESTROY()'ed. So it is guaranteed to trigger $guard->DESTROY. And in there it applys decycle_deeply
to each reference registered.
Simple, huh?
DEPENDENCY
None except for core modules.
To handle code references correctly, you need to have PadWalker installed.
EXPORT
None by default. Please import explicitly.
METHODS
- new
- add
-
see "SYNOPSIS"
SUBROUTINES
may_leak($obj)
checks if $obj
may leak. That is, contains a circular reference that is not weak.
has_cyclic_ref($obj)
checks if cyclic reference exists in $obj
.
decycle_deeply($obj)
undef
s all duplicate references in $obj
thus breaks all cyclic reference. Unlike weaken_deeply
, it decycles even code references. You shouldn't call it yourself; let decycle
take care of it.
weaken_deeply($obj)
weaken all duplicate references in $obj
. Unlike decycle_deeply
it leaves code references intact. So you can safely call it but you are at your own if $obj
contains a code reference that cycles.
recsub { }
Consider the code below:
my $fact;
$fact = sub { $_[0] <= 1 ? 1 : $_[0] * $fact->($_[0]-1) };
This leaks since $fact is now a cyclic reference. with the combination of recsub
and $CALLEE
, you can rewrite the code as:
my $fact = recsub { $_[0] <= 1 ? 1 : $_[0] * $CALLEE->($_[0]-1) };
To use this feature, you should import both recsub
and $CALLEE
as:
use Data::Decycle qw(recsub $CALLEE);
or import just recsub
and define your own $CALLEE
:
use Data::Decycle qw(recsub);
our $CALLEE;
Unlike the previous example, this one dow not leak. See Sub::Recursive for more complicated examples such as mutually recursive subrefs.
AUTHOR
Dan Kogai, <dankogai+cpan at gmail.com>
BUGS
Please report any bugs or feature requests to bug-data-decycle at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Data-Decycle. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Data::Decycle
You can also look for information at:
RT: CPAN's request tracker
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
Search CPAN
ACKNOWLEDGEMENTS
- PadWalker
-
You need this if you want to handle code references properly. When you don't have one this module simply does nothing when it encounters them.
- Devel::Cycle
-
Good for inspection -- rather overkill. Decycling features missing.
LICENSE AND COPYRIGHT
Copyright 2010 Dan Kogai.
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.