How to copy an object created by fields::new

M

Mintcake

What is the best way to make a copy (not a deep copy) of an object
created by fields::new?

My complete example program is below.

+++++++++++++++++++++++++++++++++++++++++
#!/usr/local/bin/perl -wl

use strict;

package Ftest;

use fields qw(foo bar xyzzy);

sub new
{
my $class = shift;
my __PACKAGE__ $self = fields::new($class);
@$self{qw(foo bar xyzzy)} = @_;
return $self;
}

package main;

my Ftest $f = new Ftest 1,2,3;
my Ftest $g = new Ftest;

@$g = @$f;
---------------------------------------------------------------------------------
The last line above does the actual copy. This version works with 5.8
as the fields implementation uses pseudo-hashes but it doesn't work
with 5.9 because of course they are not array references. If I change
it to...

%$g = %$f

.... then it works on both Perls but I get a warning about pseudo-hashes
being deprecated on the 5.8 version.

I am reluctant to include a no warnings 'deprecated'; statement. There
is a good reason for this: One of the mistakes I frequently make, when
declaring a scalar and assigning to it a reference to such an object,
is to forget the type, i.e.

my $foo = shift;

instead of

my Type $foo = shift;

The warning about deprecated pseudio-hashes catches this which I find
very useful.

I particularly want my code to work with both 5.8 and 5.10 (when it
arrives) and I don't want to put conditional code in there.
 
A

Anno Siegel

Mintcake said:
What is the best way to make a copy (not a deep copy) of an object
created by fields::new?

My complete example program is below.

+++++++++++++++++++++++++++++++++++++++++
#!/usr/local/bin/perl -wl

use strict;

package Ftest;

use fields qw(foo bar xyzzy);

sub new
{
my $class = shift;
my __PACKAGE__ $self = fields::new($class);

I'd write that

my __PACKAGE__ $self = $class->fields::new;

so that fields::new is visibly called as a class method.
@$self{qw(foo bar xyzzy)} = @_;
return $self;
}

package main;

my Ftest $f = new Ftest 1,2,3;
my Ftest $g = new Ftest;

@$g = @$f;
---------------------------------------------------------------------------------
The last line above does the actual copy. This version works with 5.8
as the fields implementation uses pseudo-hashes but it doesn't work
with 5.9 because of course they are not array references. If I change
it to...

%$g = %$f

... then it works on both Perls but I get a warning about pseudo-hashes
being deprecated on the 5.8 version.

I don't see the warning with neither 5.8.7 nor 5.9.4, but I'll pretend
it's there.
I am reluctant to include a no warnings 'deprecated'; statement. There
is a good reason for this: One of the mistakes I frequently make, when
declaring a scalar and assigning to it a reference to such an object,
is to forget the type, i.e.

my $foo = shift;

instead of

my Type $foo = shift;

The warning about deprecated pseudio-hashes catches this which I find
very useful.

Then isolate the copy process to a small scope and switch off the warning
only there. Objects should be treated as opaque entities anyhow, so
accessing them as arrays or hashes outside their class is a no-no.
Instead, add a method ("clone", say) to class Ftest:

sub clone {
my $self = shift;
my __PACKAGE__ $clone = ref( $self)->fields::new;
no warnings 'deprecated';
%$clone = %$self;
$clone;
}

Instead of saying

%$g = %$f;

use

my $g = $f->clone;

The "deprecated" warning won't appear with clone(), but will be active for
the rest of your code.

Anno
 
M

Mintcake

Anno said:
I'd write that

my __PACKAGE__ $self = $class->fields::new;

so that fields::new is visibly called as a class method.
I agree - much nicer

I don't see the warning with neither 5.8.7 nor 5.9.4, but I'll pretend
it's there.

It's there in 5.8.0 but not in 5.8.7. This suggests that the warning
issued under these circumstances was deemed to be erroneous. There are
a couple of other situations where this warning used to be issued but
is no longer, e.g.

keys %$g
values %$g
$g->{$key}
Then isolate the copy process to a small scope and switch off the warning
only there. Objects should be treated as opaque entities anyhow, so
accessing them as arrays or hashes outside their class is a no-no.
Instead, add a method ("clone", say) to class Ftest:

sub clone {
my $self = shift;
my __PACKAGE__ $clone = ref( $self)->fields::new;
no warnings 'deprecated';
%$clone = %$self;
$clone;
}

Instead of saying

%$g = %$f;

use

my $g = $f->clone;

The "deprecated" warning won't appear with clone(), but will be active for
the rest of your code.
I know I called them objects in the original post but they're not
really. What I really wanted were restricted hashes with the added
benefit of compile time key checking. I wanted to let users manipulate
them as if they were hashes rather than hiding anything behind a method
interface (other than the constructor of course)

Incidentally, in your solution, the temporary lexical $clone is 'typed'
but the $g scalar is not. It should be the other way around. There is
no benefit in compile time key checking when no keys appear in the
code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,539
Members
45,024
Latest member
ARDU_PROgrammER

Latest Threads

Top