Using lock_keys with strict and bless

L

lroland

Hi

I am trying to use "lock_keys" to create a unmodifiable perl module -
my code is inspired by this article
http://perltraining.com.au/tips/2005-01-31.html - I figured that if I
do the following in my constructor then it works:

----------------
my %i = ( name => undef );
bless \%i, 'Misc';
lock_keys(%i);
----------------

unfortunately this is not very handy in a perl module rather than using
a explicit hash I would like to use a reference in the style of:

----------------
package Misc::Misc;

use diagnostics;
use warnings;
use strict;

use Hash::Util qw(lock_keys);
use Data::Dumper;

sub new
{
my ($this) = @_;
bless {
name => undef,
info => {
age => undef
}
}, $this;
lock_keys(%$this); # this does not work
}

sub print
{
my ($this) = @_;
my $content = Dumper($this);
print "Hash content is:\n $content";
}

1;
----------------

This does however not work, when trying to use the module:

----------------
#!/usr/bin/perl
use strict;
use Misc::Misc;

my $test = Misc::Misc->new();
$test->{name} = "foobar";
$test->{info}->{age} = "42";
$test->print();
 
P

Paul Lalli

----------------
package Misc::Misc;

use diagnostics;
use warnings;
use strict;

use Hash::Util qw(lock_keys);
use Data::Dumper;

sub new
{
my ($this) = @_;
bless {
name => undef,
info => {
age => undef
}
}, $this;
lock_keys(%$this); # this does not work
}

You seem to be confused about how Perl modules work. In your code,
$this is the class of which new() is a member. You cannot dereference
$this as a hash, because it isn't a hash reference - or any reference
for that matter. Your bless() statement blesses an anonymous hash into
the class $class. Unfortunately, you did not provide any method by
which to store this hash reference.

Furthermore, you also apparently haven't read the documentation for the
function your using.
http://search.cpan.org/~nwclark/perl-5.8.7/lib/Hash/Util.pm#Restricted_hashes
tells us: "Note: the current implementation prevents the hash from
being bless()ed while it is in a locked state. Any attempt to do so
will raise an exception. Of course you can still bless() the hash
before you call lock_keys() so this shouldn't be a problem."

This does however not work, when trying to use the module:

----------------
#!/usr/bin/perl
use strict;
use Misc::Misc;

my $test = Misc::Misc->new();
$test->{name} = "foobar";
$test->{info}->{age} = "42";
$test->print();

Please read the positing guidelines for this group. Specifically, do
not give us the general theme of the error message - copy and paste the
*exact* error message. Had you taken the time to look at what that
error message actually told you: "Can't use string ("Misc::Misc") as a
HASH ref while "strict refs" in use at Misc.pm line 19 (#1)", you might
have been able to figure out that this error message had nothing to do
with the lock_keys method.
- so does anyone know
if lock_keys somehow can be used in a perl module such as the one
created above ?

Not the way you created it above, no. Again, read the documentation
for the function you created, and then apply it to the standard way a
constructor is written.
Step 1: get the class name
Step 2: create the hash reference
Step 3: bless the hash reference into the class
Step 4: lock the keys of the hash the reference references
Step 5: return the blessed reference

checkout
perldoc perlmod
perldoc perlobj
perldoc perloot

for more information on creating classes.

Hope this helps,
Paul Lalli
 
L

lroland

Paul said:
You seem to be confused about how Perl modules work. In your code,
$this is the class of which new() is a member. You cannot dereference
$this as a hash, because it isn't a hash reference - or any reference
for that matter. Your bless() statement blesses an anonymous hash into
the class $class. Unfortunately, you did not provide any method by
which to store this hash reference.

Thanks for the reply - if I however change the code to

-----------
package Misc::Misc;
use strict;
use Data::Dumper;
use Hash::Util qw(lock_keys);

sub new
{
my ($this) = @_;
my $struct = {
name => undef,
info => {
age => undef
}
};
bless $struct, $this;
lock_keys(%$struct);
}
sub print
{
my ($this) = @_;
my $content = Dumper($this);
print "Hash content is:\n $content";
}
1;
-----------

When I try to execute the following code:

-----------
use Misc::Misc;
my $test = Misc::Misc->new();
$test->{name} = "foobar";
$test->{info}->{age} = "42";
$test->print();
-----------

then I get the following error: "can't call method "print" on unblessed
reference at misc.pl" - I realize that I have misunderstood something
important here, but I have googled around, read the man pages and none
of them seams to deal with locking the content of a hash used
internally in a module (although some of them mention it is possible).
Furthermore, you also apparently haven't read the documentation for the
function your using.
http://search.cpan.org/~nwclark/perl-5.8.7/lib/Hash/Util.pm#Restricted_hashes
tells us: "Note: the current implementation prevents the hash from
being bless()ed while it is in a locked state. Any attempt to do so
will raise an exception. Of course you can still bless() the hash
before you call lock_keys() so this shouldn't be a problem."

Well I may be off (still new to OO perl) but the docs says "Of course
you can still bless() the hash before you call lock_keys()" - when I
do:
 
P

Paul Lalli

-----------
package Misc::Misc;
use strict;
use Data::Dumper;
use Hash::Util qw(lock_keys);

sub new
{
my ($this) = @_;
my $struct = {
name => undef,
info => {
age => undef
}
};
bless $struct, $this;
lock_keys(%$struct);
}

I'm going to paste back in my "five steps" that you snipped:
You have skipped step 5.
When I try to execute the following code:

-----------
use Misc::Misc;
my $test = Misc::Misc->new();
$test->{name} = "foobar";
$test->{info}->{age} = "42";
$test->print();
-----------

then I get the following error: "can't call method "print" on unblessed
reference at misc.pl" - I realize that I have misunderstood something
important here, but I have googled around, read the man pages and none
of them seams to deal with locking the content of a hash used
internally in a module (although some of them mention it is possible).

That's because this has nothing to do with locking. You're assigning
$test to be the return value of the call to new(). Perl subroutines
return the last statement evaluated. In this case, that's the
lock_keys() call. lock_keys() does not return your blessed reference.
So $test does not contain your blessed reference. Add this line to the
end of your constructor:

return $struct;
Well I may be off (still new to OO perl) but the docs says "Of course
you can still bless() the hash before you call lock_keys()" - when I
do:

That section of the code is 100% fine. Nothing wrong there.

Paul Lalli
 
L

lroland

Paul said:
I'm going to paste back in my "five steps" that you snipped:

That's because this has nothing to do with locking. You're assigning
$test to be the return value of the call to new(). Perl subroutines
return the last statement evaluated. In this case, that's the
lock_keys() call. lock_keys() does not return your blessed reference.
So $test does not contain your blessed reference. Add this line to the
end of your constructor:

return $struct;

Thanks a lot for your help - I think I get your point - I seam to fail
seeing the relationship between the internal struct and the
encapsulation performed by bless - this is somewhat different to what I
am used to in C++. I will try to get my hands on the OO perl book by
Damian Conway so I can get this right.

changing the module to:

------------
package Misc::Misc;
use strict;
use Data::Dumper;
use Hash::Util qw(lock_keys);

sub new
{
my ($this) = @_;
my $struct = {
name => undef,
info => {
age => undef
}
};
bless $struct, $this;
lock_keys(%$struct);
return $struct;
}
sub print
{
my ($this) = @_;
my $content = Dumper($this);
print "Hash content is:\n $content";
}
1;
------------

does indeed make the code work but it does not ensure that the entire
data structure is locked - if I execute the following code:

------------
#/usr/bin/perl
use Strict;
use Misc::Misc;
my $test = Misc::Misc->new();
# change values is ok
$test->{name} = "foobar";
$test->{info}->{age} = "42";
# this should not could happen
$test->{info}->{fail} = "12";
#print object
$test->print();
------------

then the resulting structure contains a key which was not created in in
the constructor (i.e. {info}->{fail} )- Without having researched this
further I am guessing that I it is because I am using a hash of hashes
and lock_keys properly only function on the outer hash not the inner
one.
 
P

Paul Lalli

Thanks a lot for your help - I think I get your point - I seam to fail
seeing the relationship between the internal struct and the
encapsulation performed by bless - this is somewhat different to what I
am used to in C++. I will try to get my hands on the OO perl book by
Damian Conway so I can get this right.

My appreciation for Mr. Conway's work notwithstanding, I would still
recommend reading the free documentation that comes with Perl before
spending money on a book. See if you can understand how to make your
code work by reading the various references and tutorials.

perldoc perlref - Reference about references
perldoc perllol - Building "lists of lists"
perldoc perldsc - Data Structures Cookbook
perldoc perlobj - Perl objects
perldoc perltoot - Object Oriented Tutorial
perldoc perlmod - Modules and Namespaces
perldoc perlnewmod - Writing your own new module

sub new
{
my ($this) = @_;
my $struct = {
name => undef,
info => {
age => undef
}
};
bless $struct, $this;
lock_keys(%$struct);
return $struct;
}
does not ensure that the entire data structure is locked - if I
execute the following code:

------------
#/usr/bin/perl
use Strict;
use Misc::Misc;
my $test = Misc::Misc->new();
# change values is ok
$test->{name} = "foobar";
$test->{info}->{age} = "42";
# this should not could happen
$test->{info}->{fail} = "12";
#print object
$test->print();
------------

then the resulting structure contains a key which was not created in in
the constructor (i.e. {info}->{fail} )- Without having researched this
further I am guessing that I it is because I am using a hash of hashes
and lock_keys properly only function on the outer hash not the inner
one.

Mostly Correct. The hash that you locked contains exactly 2 keys -
'name' and 'info'. The *value* of the hash at the key 'info' is a
reference to another hash. That hash is not locked. You could, of
course, lock it:
lock_keys(%{$struct->{info}});

Paul Lalli
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top