perlquestion
zzspectrez
<P>I am looking for a little enlightenment about typeglobs. I am writing module that stores a filehandle in an object. The filehandle is actually a filehandle to a socket but lets pretend it is just a file. This can be done doings something like the following. </P>
<CODE>
sub foo {
my $self = shift;
my $filename = shift;
open FH, $filename or die "Can not open file: $!\n";
$self->{'fh'} = \*FH;
}
</CODE>
<P>This has worked fine. However, now I need to store multiple filehandles in the object and I want one subroutine to handle opening the files and returning the filehandle. Since a typeglob holds an entire symbol table entry, I know that trying to open all the files with <CODE>FH</CODE> and returning a typeglob will not work.</P>
<P>So I do what I allways do when Im not sure what will hapen, I make a test applications. This is what I get.</P>
<CODE>
package FOO;
use diagnostics;
sub new {
my $class = shift;
my $self = { blahh => 'blah', };
my (%opts) = @_;
return unless (exists $opts{'input'} && exists $opts{'output'});
%$self = ( %$self, %opts );
bless $self, $class;
$self->{'inp_fh'} = $self->_open_read;
$self->{'out_fh'} = $self->_open_write;
return $self;
}
sub read_line {
my $self = shift;
my $fh = $self->{'inp_fh'};
return <$fh>;
}
sub write_line {
my $self = shift;
my $data = shift;
my $fh = $self->{'out_fh'};
print $fh $data;
}
sub _open_read {
my $self = shift;
{
local *FH;
open FH, $self->{'input'} or die "Couldn't open: $!\n";
return \*FH;
}
}
sub _open_write {
my $self = shift;
{
local *FH;
open FH, ">$self->{'output'}" or die "Couldn't open file: $!\n";
return \*FH;
}
}
sub DESTROY {
my $self = shift;
close ($self->{'inp_fh'});
close ($self->{'out_fh'});
}
package main;
use strict;
my $data;
my $foo = FOO->new('input'=>"inp.dat", 'output'=>"out.dat") ||
die ("Error creating new FOO!!\n");
for my $key (keys %$foo) {
print "KEY: $key \tVAL: $foo->{$key}\n";
}
while ($data = $foo->read_line) {
$foo->write_line($data);
}
</CODE>
<P>This works fine, making a duplicate of the file, and returning data like the following:</P>
<CODE>
KEY: out_fh VAL: GLOB(0x1a75098)
KEY: inp_fh VAL: GLOB(0x1a75038)
KEY: input VAL: inp.dat
KEY: blahh VAL: blah
KEY: output VAL: out.dat
</CODE>
<P>Making slight modifications. Changing <code>_open_read()</code> and <code>_open_write()</code> so that the return is <code>return *FH</code>. I run this and the program has the same desired results.. The output of the filehandle keys changes to: </P>
<CODE>
KEY: out_fh VAL: *FOO::FH
KEY: inp_fh VAL: *FOO::FH
</CODE>
<P>In my mind, I did not think this would work. Since <code>FH</code> goes out of scope at the end of the block, you would think you would need a reference to the typeglob to maintain access to it. Secondly, in the second example the object appears to have two references to the same filehandle <code>*FOO::FH</code>. However although they are opened as the same filehandle, the code operates on two seperate filehandles. How is this working? </P>
<P>Lastly, I remember reading an article by [id://3737|Mark-Jason Dominus] in Perl Journal on the uses of local. At the time I did not understand much about typeglobs or the diferences between <code>local</code> and <code>my</code> so I only glanced at the article. Looking back at the <a href="http://www.tpj.com/issues/vol4_2/tpj0402-0004.html">article</a> I find an interesting tidbit that relates</P>
<P>The article sugests using the following construct to get a glob that is disconected from the symbol table: <code>$fh = do { local *FH };</code>. Then you use <code>$fh</code> as if it was a filehandle.</P>
<P>So I modify my test program like the following: </P>
<CODE>
sub _open_write {
my $self = shift;
my $fh = do { local *FH };
open $fh, ">$self->{'output'}" or die "Couldn't open file: $!\n";
return $fh;
}
</CODE>
<P>This works great! However, once again Im a little confused about how this trick works. First step I open my camel book and refresh reading about <code>do</code>. Ok, I feel stupid, I didnt know that a do block return the value of the last expression evaluated. This brings a little enlightenment. However, once again Im confused on how the block can return the value of *FH which loses scope once the block exits.</P>
<P>I guess part of the confusion is, what exactly is being returned?!? According to the print of my hash, both variables point to the same glob <CODE>*FOO::FH</CODE> which was local in scope.</P>
<P>Thanks!<br><strong>zzSPECTREz</strong></P>