[ Team LiB ] Previous Section Next Section

Recipe 13.7 Copy Constructors

13.7.1 Problem

You would like to provide users of your class with a copy method, or you would like to copy an object for which no copy method has been provided by the class.

13.7.2 Solution

Use the dclone( ) function from the standard Storable module.

use Storable qw(dclone);
use Carp;
sub copy {
     my $self = shift;
     croak "can't copy class $self" unless ref $self;
     my $copy = Storable::dclone($self);
     return $copy;
}

13.7.3 Discussion

As described in Recipe 11.12, the Storable module's dclone function will recursively copy (virtually) any data structure. It works on objects, too, correctly giving you back new objects that are appropriately blessed. This assumes that the underlying types are SCALAR, ARRAY, HASH, or CODE refs. Things like GLOB and IO refs won't serialize.

Some classes already provide methods to copy their objects; others do not, not so much out of intent as out of neglect. Consider this:

sub UNIVERSAL::copy {
    my $self = shift;
    unless (ref $self) { 
        require Carp;
        Carp::croak("can't copy class $self");
    }
    require Storable;
    my $copy = Storable::dclone($self);
    return $copy;
}

Now all objects can be copied, providing they're of the supported types. Classes that provide their own copy methods are unaffected, but any class that doesn't provide its own copy method will pick up this definition. We placed the require on Storable within the function call itself so that you load Storable only if you actually plan to use it. Likewise, we placed the one for Carp inside the test that will end up using it. By using require, we delay loading until the module is actually needed.

We also avoid use because it would import things into our current package. This could be antisocial. From the previous code snippet, you cannot determine what package you're even in. Just because we've declared a subroutine named copy to be in package UNIVERSAL doesn't mean that the code within that subroutine is in package UNIVERSAL. Rather, it's in whatever package we are currently compiling into.

Some folks would argue that we're being outrageously cavalier by interjecting a function into somebody else's namespace like that—especially into all possible class namespaces, as it's in UNIVERSAL. Cavalier perhaps, but hardly outrageously so; after all, UNIVERSAL is there to be used. It's no holy namespace, sacrosanct against any change. Whether this ends up being a very stupid thing or a very clever thing is not up to Perl to decide, or prevent.

13.7.4 See Also

Recipe 11.12; Recipe 13.9; the documentation for the standard Storable modules; the section on Inheritance in the introduction to this chapter; the section on "UNIVERSAL: The Ultimate Ancestor Class" in Chapter 12 of Programming Perl

    [ Team LiB ] Previous Section Next Section