10

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 :)

vol7ron
  • 40,809
  • 21
  • 119
  • 172
  • Accepted solution may change: note that it is more for shallow copying simple data structures, but would not solve deep copying, or even more advanced class structures; which was my original question. Keeping that in mind, the selected answer may change in the future. – vol7ron May 09 '13 at 15:43

5 Answers5

15

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;
}
choroba
  • 231,213
  • 25
  • 204
  • 289
  • I think this might be the way to go. This would have been my next step, I was hoping Perl had something built-in that did the same thing, so that I wouldn't have to bloat my object, but thanks! – vol7ron May 09 '13 at 15:20
  • Additionaly `my $dictionary = clone $book;` reads a lot better. Although this is the accepted answer, users should note that this is more for shallow copies of simple data structures, and would not solve deep copying, or even more advanced class structures. – vol7ron May 09 '13 at 15:39
  • 4
    The `bless { %$self }, ref $self` technique will only do a shallow copy. Attributes of `$self` which are references will not be cloned. For example, `$obj->{ponies} = [qw(Dash Sparkle Jack)]; $clone = $obj->clone; push @{$clone->{ponies}}, "Pinkie"` will modify both objects. – Schwern May 09 '13 at 18:47
  • @Schwern: Yes. I take deep cloning as one of the "dirty tricks" you have to use. – choroba May 09 '13 at 19:44
  • @Schwern: that's a nice example of how it's a shallow copy. Though, in my comment a few hours before (here and the question comment), I mentioned how this is the accepted answer for shallow copies. I'll probably end up heeding advice for using `Clone` or `Storable`, but I feel if I start using modules, I might as well Moosify it. – vol7ron May 09 '13 at 21:00
10

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).

Zaid
  • 36,680
  • 16
  • 86
  • 155
4

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.

Schwern
  • 153,029
  • 25
  • 195
  • 336
  • I would say that sometimes you'd *want* the two cloned objects to stay pointing to the same referent. For example if your `Person` class had an `employer` attribute pointing to an object representing the AcmeCorp `Organization`, you probably wouldn't want cloning Bob to also clone AcmeCorp. These kind of decisions can only be made on a class-by-class basis, and should be thoroughly documented. – tobyink Feb 21 '14 at 10:08
  • @tobyink Yes, exactly why cloning is done via `$obj->clone` rather than `clone($obj)`. The object can best decide how it should be cloned. What a `clone` method should do is ill defined. Is it a shallow clone? A deep clone? An "as I see fit" clone? The behavior/interface of your clone method should be thought out and defined. You may need several clone methods. – Schwern Feb 25 '14 at 21:47
3

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
    );
}
darch
  • 4,200
  • 1
  • 20
  • 23
0

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";
eric.xiao
  • 11
  • 2
  • Hi,Rahil Wazir,there are website URL:http://search.cpan.org/~ams/Storable-2.45/Storable.pm , you can find out:"Storable provides you with a dclone interface which does not create that intermediary scalar but instead freezes the structure in some internal memory space and then immediately thaws it out.", "The heart of Storable is written in C for decent speed. Extra low-level optimizations have been made when manipulating perl internals, to sacrifice encapsulation for the benefit of greater speed."; I hope that things could help you. – eric.xiao Feb 02 '14 at 12:12
  • 2
    Can you edit this part into your answer instead of commenting. – Rahil Wazir Feb 02 '14 at 13:17