Question

I'm looking to do a deep (at this point, shallow may suffice) copy of a blessed object.

Foo Class

package Foo;
our $FOO = new Foo;       # initial run

sub new {
   my $class = shift;
   my $self  = {};
   bless $self, $class;
   return $self;
}

Main Program

use Foo;
my $copy = $Foo::FOO;     # instead of creating a ref, want to deep copy here
$copy->{bar} = 'bar';

bar appears in both $Foo::FOO and $copy. I realize I could create a copy of the object by setting it up as $copy = { %{$Foo::FOO} }, but then it would no longer be blessed; additionally, this would only work for simple data structures (right now not an issue). Is the only way to copy this way and then bless after (eg $copy = bless { %{$Foo::FOO} }, q{Foo};)?

I'm trying to avoid using Moose, Clone, or other non-Core modules/packages, so please keep that in mind when replying. Bolded so it stands out more :)

Was it helpful?

Solution

The copying should be part of the API. The user of your module would never know what special actions are required upon creation of a new object (consider registering each object in a my hash in your package).

Therefore, provide a clone method for your objects. Inside it, you can use any dirty tricks you like:

sub clone {
    my $self = shift;
    my $copy = bless { %$self }, ref $self;
    $register{$copy} = localtime; # Or whatever else you need to do with a new object.
    # ...
    return $copy;
}

OTHER TIPS

use Storable 'dclone'; ?

$ corelist Storable

Storable was first released with perl v5.7.3

Plus you can fiddle around with Storable hooks to assert finer control over copying your objects (not that I've done it, but that's what the documentation claims).

my $copy = bless { %$self }, ref $self; in @chorba's answer is insufficient. It will only clone the first layer. Any references stored in $self will not be cloned. The consequences of this is...

$obj->{ponies} = [qw(Dash Sparkle Jack)];
$clone = $obj->clone;
push @{$clone->{ponies}}, "Pinkie";
print join ", ", @{$obj->{ponies}};  # Dash Sparkle Jack Pinkie

You might not have any references now, but you probably will later. Or somebody else will stick one into your object. Or they'll subclass and add one.

You can write a deep cloning routine, but it's not simple. I would highly recommend using Clone. It has no dependencies and so you can simply copy Clone.pm into your project.

The other alternative is Storable::dclone, mentioned by @Zaid, which has been in core for a long time.

No matter what you use, providing a clone method on your class is the right thing to do, even if it's just a wrapper around Clone or Storable::dclone. This will shield the user of your object from the details of how your object is cloned.

There is no good way for the calling program to know what "copying an object" entails, so an object should know how to copy itself. Perl's OO doesn't provide you any help here, but the conventional thing to do is something like this:

package Car;

sub clone {
    my ($self) = @_;

    return $self->new(
        ( map { $_ => $self->$_() } qw/make model/ ), # built-in types
        engine => $self->engine->clone(), # copying an object
    );
}

I am sorry, I can't notice this sentence:

*I'm trying to avoid using Moose, Clone, or other non-Core modules/packages, so please keep that in mind when replying. Bolded so it stands out more :) *

so this answer could not be accepted!

#!/usr/bin/env perl -w
use strict;
use warnings;
use Storable;
use Data::Dumper;

my $src = {
  foo => 0,
  bar => [0,1]
};

$src -> {baz} = $src;
my $dst = Storable::dclone($src);
print '$src is : '.Dumper($src)."\n";
print '$dst is : '.Dumper($dst)."\n";
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top